mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-04 04:47:52 +01:00
Merge branch 'main' of https://github.com/Ranchero-Software/NetNewsWire into main
This commit is contained in:
commit
ecf944c80a
@ -320,7 +320,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
}
|
||||
|
||||
func application(_ sender: NSApplication, openFile filename: String) -> Bool {
|
||||
guard filename.hasSuffix(".nnwtheme") else { return false }
|
||||
guard filename.hasSuffix(ArticleTheme.nnwThemeSuffix) else { return false }
|
||||
importTheme(filename: filename)
|
||||
return true
|
||||
}
|
||||
@ -812,36 +812,71 @@ private extension AppDelegate {
|
||||
attrs[.font] = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
|
||||
attrs[.foregroundColor] = NSColor.textColor
|
||||
|
||||
let titleParagraphStyle = NSMutableParagraphStyle()
|
||||
titleParagraphStyle.alignment = .center
|
||||
attrs[.paragraphStyle] = titleParagraphStyle
|
||||
if #available(macOS 11.0, *) {
|
||||
let titleParagraphStyle = NSMutableParagraphStyle()
|
||||
titleParagraphStyle.alignment = .center
|
||||
attrs[.paragraphStyle] = titleParagraphStyle
|
||||
}
|
||||
|
||||
let websiteText = NSMutableAttributedString()
|
||||
websiteText.append(NSAttributedString(string: NSLocalizedString("Author's Website", comment: "Author's Website"), attributes: attrs))
|
||||
websiteText.append(NSAttributedString(string: "\n"))
|
||||
|
||||
if #available(macOS 11.0, *) {
|
||||
websiteText.append(NSAttributedString(string: "\n"))
|
||||
} else {
|
||||
websiteText.append(NSAttributedString(string: " "))
|
||||
}
|
||||
|
||||
attrs[.link] = theme.creatorHomePage
|
||||
websiteText.append(NSAttributedString(string: theme.creatorHomePage, attributes: attrs))
|
||||
|
||||
let textView = NSTextView(frame: CGRect(x: 0, y: 0, width: 200, height: 15))
|
||||
let textViewWidth: CGFloat
|
||||
if #available(macOS 11.0, *) {
|
||||
textViewWidth = 200
|
||||
} else {
|
||||
textViewWidth = 400
|
||||
}
|
||||
|
||||
let textView = NSTextView(frame: CGRect(x: 0, y: 0, width: textViewWidth, height: 15))
|
||||
textView.isEditable = false
|
||||
textView.drawsBackground = false
|
||||
textView.textStorage?.setAttributedString(websiteText)
|
||||
alert.accessoryView = textView
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Install Style", comment: "Install Style"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Install Style"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Install Theme", comment: "Install Theme"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Install Theme"))
|
||||
|
||||
alert.beginSheetModal(for: window) { [weak self] result in
|
||||
func importTheme() {
|
||||
do {
|
||||
try ArticleThemesManager.shared.importTheme(filename: filename)
|
||||
confirmImportSuccess(themeName: theme.name)
|
||||
} catch {
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
alert.beginSheetModal(for: window) { result in
|
||||
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
||||
guard let self = self else { return }
|
||||
|
||||
do {
|
||||
try ArticleThemesManager.shared.importTheme(filename: filename)
|
||||
self.confirmImportSuccess(themeName: theme.name)
|
||||
} catch {
|
||||
NSApplication.shared.presentError(error)
|
||||
|
||||
if ArticleThemesManager.shared.themeExists(filename: filename) {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .warning
|
||||
|
||||
let localizedMessageText = NSLocalizedString("The theme “%@” already exists. Overwrite it?", comment: "Overwrite theme")
|
||||
alert.messageText = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name) as String
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Overwrite", comment: "Overwrite"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Install Theme"))
|
||||
|
||||
alert.beginSheetModal(for: window) { result in
|
||||
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
||||
importTheme()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
importTheme()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +157,18 @@
|
||||
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy Article URL" id="qNk-By-jKp">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="copyArticleURL:" target="Ady-hI-5gd" id="A5t-x7-Mmy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy External URL" id="fOF-99-6Iv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="copyExternalURL:" target="Ady-hI-5gd" id="MkV-Do-Bc1"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -196,6 +196,24 @@
|
||||
<action selector="cleanUp:" target="Oky-zY-oP4" id="UCH-DG-yk4"/>
|
||||
</connections>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="596363B5-CE41-417C-B8AB-11CC2C99BCA5" label="Article Theme" paletteLabel="Article Theme" sizingBehavior="auto" id="3Hc-al-vK2">
|
||||
<nil key="toolTip"/>
|
||||
<popUpButton key="view" verticalHuggingPriority="750" id="MFT-nb-eLG">
|
||||
<rect key="frame" x="0.0" y="14" width="100" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="roundTextured" title="Item 1" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" selectedItem="xAs-IL-tMv" id="ior-Gb-LTq">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="1Ac-Uq-Yeu">
|
||||
<items>
|
||||
<menuItem title="Item 1" state="on" id="xAs-IL-tMv"/>
|
||||
<menuItem title="Item 2" id="T8e-ib-OTb"/>
|
||||
<menuItem title="Item 3" id="Gfy-76-Njz"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
</toolbarItem>
|
||||
</allowedToolbarItems>
|
||||
<defaultToolbarItems>
|
||||
<toolbarItem reference="Skp-5r-70Q"/>
|
||||
@ -221,6 +239,7 @@
|
||||
</connections>
|
||||
</window>
|
||||
<connections>
|
||||
<outlet property="articleThemePopUpButton" destination="MFT-nb-eLG" id="lHc-ej-PrT"/>
|
||||
<segue destination="reS-fe-pD8" kind="relationship" relationship="window.shadowedContentViewController" id="WS2-WB-dc4"/>
|
||||
</connections>
|
||||
</windowController>
|
||||
|
@ -32,14 +32,14 @@
|
||||
<objects>
|
||||
<viewController title="General" storyboardIdentifier="General" id="iuH-lz-18x" customClass="GeneralPreferencesViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" misplaced="YES" id="WnV-px-wCT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="509" height="306"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="509" height="339"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ut3-yd-q6G">
|
||||
<rect key="frame" x="54" y="16" width="399" height="274"/>
|
||||
<rect key="frame" x="54" y="16" width="399" height="306"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pR2-Bf-7Fd">
|
||||
<rect key="frame" x="6" y="253" width="105" height="16"/>
|
||||
<rect key="frame" x="6" y="285" width="105" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Article Text Size:" id="xQu-QV-91i">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -47,7 +47,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Z6O-Zt-V1g">
|
||||
<rect key="frame" x="114" y="246" width="289" height="25"/>
|
||||
<rect key="frame" x="114" y="278" width="289" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Medium" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2" imageScaling="proportionallyDown" inset="2" selectedItem="jMV-2o-5Oh" id="6pw-Vq-tjM">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -76,7 +76,7 @@
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ISO-Wu-R60">
|
||||
<rect key="frame" x="114" y="212" width="289" height="25"/>
|
||||
<rect key="frame" x="114" y="244" width="289" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Default" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Pkl-EA-Goa" id="vN9-pm-Gls">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -90,6 +90,16 @@
|
||||
<action selector="articleThemePopUpDidChange:" target="iuH-lz-18x" id="afz-Uu-a5b"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1w0-nA-DEO">
|
||||
<rect key="frame" x="110" y="207" width="217" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Open Themes Folder in Finder" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ySX-5i-SP1">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showThemesFolder:" target="iuH-lz-18x" id="WEP-Fe-cAR"/>
|
||||
</connections>
|
||||
</button>
|
||||
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="Tdg-6Y-gvW">
|
||||
<rect key="frame" x="0.0" y="197" width="399" height="5"/>
|
||||
</box>
|
||||
@ -223,7 +233,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="S2Z-bG-jYk">
|
||||
<rect key="frame" x="18" y="219" width="93" height="16"/>
|
||||
<rect key="frame" x="18" y="251" width="93" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Article Theme:" id="MQe-Za-N8J">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -255,6 +265,7 @@
|
||||
<constraint firstItem="j0t-Wa-UTL" firstAttribute="leading" secondItem="Ubm-Pk-l7x" secondAttribute="leading" constant="19" id="UKq-8p-lyR"/>
|
||||
<constraint firstItem="j0t-Wa-UTL" firstAttribute="top" secondItem="Ubm-Pk-l7x" secondAttribute="bottom" constant="8" id="XTw-Ef-FD3"/>
|
||||
<constraint firstItem="wtY-Zd-Ps9" firstAttribute="trailing" secondItem="SFF-mL-yc8" secondAttribute="trailing" id="Zkn-zv-as5"/>
|
||||
<constraint firstItem="1w0-nA-DEO" firstAttribute="top" secondItem="ISO-Wu-R60" secondAttribute="bottom" constant="14" id="ZlG-V3-AAd"/>
|
||||
<constraint firstItem="pR2-Bf-7Fd" firstAttribute="firstBaseline" secondItem="Z6O-Zt-V1g" secondAttribute="firstBaseline" id="aO5-iE-L7A"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Z6O-Zt-V1g" secondAttribute="trailing" id="aS9-KA-vSH"/>
|
||||
<constraint firstItem="wtY-Zd-Ps9" firstAttribute="top" secondItem="j0t-Wa-UTL" secondAttribute="bottom" constant="14" id="aod-td-Gim"/>
|
||||
@ -262,13 +273,14 @@
|
||||
<constraint firstItem="Tdg-6Y-gvW" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="b3I-JF-If3"/>
|
||||
<constraint firstItem="wtY-Zd-Ps9" firstAttribute="leading" secondItem="SFF-mL-yc8" secondAttribute="leading" id="dTq-cu-Z2s"/>
|
||||
<constraint firstItem="Yrc-6Q-kx8" firstAttribute="trailing" secondItem="wtY-Zd-Ps9" secondAttribute="trailing" id="e6V-q6-WJq"/>
|
||||
<constraint firstItem="Tdg-6Y-gvW" firstAttribute="top" secondItem="ISO-Wu-R60" secondAttribute="bottom" constant="16" id="eDO-fC-gaC"/>
|
||||
<constraint firstItem="SFF-mL-yc8" firstAttribute="top" secondItem="hQy-ng-ijd" secondAttribute="bottom" constant="16" id="eM7-OM-Qsz"/>
|
||||
<constraint firstItem="Yrc-6Q-kx8" firstAttribute="leading" secondItem="wtY-Zd-Ps9" secondAttribute="leading" id="gNX-Yc-DdD"/>
|
||||
<constraint firstItem="1w0-nA-DEO" firstAttribute="leading" secondItem="ISO-Wu-R60" secondAttribute="leading" id="gWR-OU-qcO"/>
|
||||
<constraint firstItem="Ci4-fW-KjU" firstAttribute="top" secondItem="Tdg-6Y-gvW" secondAttribute="bottom" constant="16" id="hXl-1D-lTD"/>
|
||||
<constraint firstItem="wtY-Zd-Ps9" firstAttribute="leading" secondItem="yrg-M3-Dbz" secondAttribute="trailing" constant="6" symbolic="YES" id="hpP-sx-veV"/>
|
||||
<constraint firstItem="hQy-ng-ijd" firstAttribute="top" secondItem="Yrc-6Q-kx8" secondAttribute="bottom" constant="16" id="i2g-cZ-EV4"/>
|
||||
<constraint firstItem="ucw-vG-yLt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="lDL-JN-ANP"/>
|
||||
<constraint firstItem="Tdg-6Y-gvW" firstAttribute="top" secondItem="1w0-nA-DEO" secondAttribute="bottom" constant="14" id="lEK-yl-TCM"/>
|
||||
<constraint firstItem="Z6O-Zt-V1g" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="noW-Jf-Xbs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Tdg-6Y-gvW" secondAttribute="trailing" id="qzz-gu-8kO"/>
|
||||
<constraint firstItem="Wsb-Lr-8Q7" firstAttribute="firstBaseline" secondItem="Ci4-fW-KjU" secondAttribute="firstBaseline" id="rPX-je-OG5"/>
|
||||
@ -295,7 +307,7 @@
|
||||
<customObject id="bSQ-tq-wd3" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<userDefaultsController representsSharedInstance="YES" id="mAF-gO-1PI"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-568.5" y="451.5"/>
|
||||
<point key="canvasLocation" x="-565.5" y="432"/>
|
||||
</scene>
|
||||
<!--Advanced Preferences View Controller-->
|
||||
<scene sceneID="z1G-rc-sP5">
|
||||
@ -475,16 +487,16 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="7UM-iq-OLB" customClass="PreferencesTableViewBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="44" width="180" height="240"/>
|
||||
<rect key="frame" x="20" y="44" width="180" height="228"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PaF-du-r3c">
|
||||
<rect key="frame" x="1" y="1" width="178" height="238"/>
|
||||
<rect key="frame" x="1" y="1" width="178" height="226"/>
|
||||
<clipView key="contentView" id="cil-Gq-akO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="238"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="226"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" tableStyle="fullWidth" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="aTp-KR-y6b">
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="238"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="226"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -591,7 +603,7 @@
|
||||
<rect key="frame" x="83" y="20" width="117" height="24"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Y7D-xQ-wep">
|
||||
<rect key="frame" x="208" y="20" width="222" height="264"/>
|
||||
<rect key="frame" x="208" y="20" width="222" height="252"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
@ -646,16 +658,16 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="pjs-G4-byk" customClass="PreferencesTableViewBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="44" width="180" height="240"/>
|
||||
<rect key="frame" x="20" y="44" width="180" height="228"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="29T-r2-ckC">
|
||||
<rect key="frame" x="1" y="1" width="178" height="238"/>
|
||||
<rect key="frame" x="1" y="1" width="178" height="226"/>
|
||||
<clipView key="contentView" id="dXw-GY-TP8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="238"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="226"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" tableStyle="fullWidth" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="dfn-Vn-oDp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="238"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="178" height="226"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -758,7 +770,7 @@
|
||||
<rect key="frame" x="83" y="20" width="117" height="24"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="N1N-pE-gBL">
|
||||
<rect key="frame" x="208" y="20" width="222" height="264"/>
|
||||
<rect key="frame" x="208" y="20" width="222" height="252"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
|
@ -55,6 +55,7 @@ final class DetailWebViewController: NSViewController {
|
||||
|
||||
private let detailIconSchemeHandler = DetailIconSchemeHandler()
|
||||
private var waitingForFirstReload = false
|
||||
private var windowScrollY: CGFloat?
|
||||
private let keyboardDelegate = DetailKeyboardDelegate()
|
||||
|
||||
private struct MessageName {
|
||||
@ -143,7 +144,10 @@ final class DetailWebViewController: NSViewController {
|
||||
}
|
||||
|
||||
@objc func currentArticleThemeDidChangeNotification(_ note: Notification) {
|
||||
reloadHTML()
|
||||
fetchScrollInfo() { scrollInfo in
|
||||
self.windowScrollY = scrollInfo?.offsetY
|
||||
self.reloadHTML()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Media Functions
|
||||
@ -226,6 +230,11 @@ extension DetailWebViewController: WKNavigationDelegate, WKUIDelegate {
|
||||
webView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
if let windowScrollY = windowScrollY {
|
||||
webView.evaluateJavaScript("window.scrollTo(0, \(windowScrollY));")
|
||||
self.windowScrollY = nil
|
||||
}
|
||||
}
|
||||
|
||||
// WKUIDelegate
|
||||
|
@ -18,7 +18,9 @@ enum TimelineSourceMode {
|
||||
|
||||
class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
|
||||
private var activityManager = ActivityManager()
|
||||
@IBOutlet weak var articleThemePopUpButton: NSPopUpButton?
|
||||
|
||||
private var activityManager = ActivityManager()
|
||||
|
||||
private var isShowingExtractedArticle = false
|
||||
private var articleExtractor: ArticleExtractor? = nil
|
||||
@ -62,6 +64,8 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
|
||||
sharingServicePickerDelegate = SharingServicePickerDelegate(self.window)
|
||||
|
||||
updateArticleThemeMenu()
|
||||
|
||||
if #available(macOS 11.0, *) {
|
||||
let toolbar = NSToolbar(identifier: "MainWindowToolbar")
|
||||
toolbar.allowsUserCustomization = true
|
||||
@ -155,11 +159,11 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
}
|
||||
|
||||
@objc func articleThemeNamesDidChangeNotification(_ note: Notification) {
|
||||
buildArticleThemeMenu()
|
||||
updateArticleThemeMenu()
|
||||
}
|
||||
|
||||
@objc func currentArticleThemeDidChangeNotification(_ note: Notification) {
|
||||
buildArticleThemeMenu()
|
||||
updateArticleThemeMenu()
|
||||
}
|
||||
|
||||
private func updateWindowTitleIfNecessary(_ noteObject: Any?) {
|
||||
@ -200,6 +204,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
|
||||
public func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
|
||||
if item.action == #selector(copyArticleURL(_:)) {
|
||||
return canCopyArticleURL()
|
||||
}
|
||||
|
||||
if item.action == #selector(copyExternalURL(_:)) {
|
||||
return canCopyExternalURL()
|
||||
}
|
||||
|
||||
if item.action == #selector(openArticleInBrowser(_:)) {
|
||||
if let item = item as? NSMenuItem, item.keyEquivalentModifierMask.contains(.shift) {
|
||||
item.title = Browser.titleForOpenInBrowserInverted
|
||||
@ -298,6 +310,18 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
||||
|
||||
}
|
||||
|
||||
@IBAction func copyArticleURL(_ sender: Any?) {
|
||||
if let link = oneSelectedArticle?.preferredURL?.absoluteString {
|
||||
URLPasteboardWriter.write(urlString: link, to: .general)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func copyExternalURL(_ sender: Any?) {
|
||||
if let link = oneSelectedArticle?.externalURL {
|
||||
URLPasteboardWriter.write(urlString: link, to: .general)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func openArticleInBrowser(_ sender: Any?) {
|
||||
if let link = currentLink {
|
||||
Browser.open(link, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false)
|
||||
@ -817,7 +841,6 @@ extension MainWindowController: NSToolbarDelegate {
|
||||
let description = NSLocalizedString("Article Theme", comment: "Article Theme")
|
||||
articleThemeMenuToolbarItem.toolTip = description
|
||||
articleThemeMenuToolbarItem.label = description
|
||||
buildArticleThemeMenu()
|
||||
return articleThemeMenuToolbarItem
|
||||
|
||||
case .search:
|
||||
@ -1033,6 +1056,14 @@ private extension MainWindowController {
|
||||
}
|
||||
|
||||
// MARK: - Command Validation
|
||||
|
||||
func canCopyArticleURL() -> Bool {
|
||||
return currentLink != nil
|
||||
}
|
||||
|
||||
func canCopyExternalURL() -> Bool {
|
||||
return oneSelectedArticle?.externalURL != nil && oneSelectedArticle?.externalURL != currentLink
|
||||
}
|
||||
|
||||
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
|
||||
|
||||
@ -1413,7 +1444,7 @@ private extension MainWindowController {
|
||||
return menu
|
||||
}
|
||||
|
||||
func buildArticleThemeMenu() {
|
||||
func updateArticleThemeMenu() {
|
||||
let articleThemeMenu = NSMenu()
|
||||
|
||||
let defaultThemeItem = NSMenuItem()
|
||||
@ -1433,6 +1464,7 @@ private extension MainWindowController {
|
||||
}
|
||||
|
||||
articleThemeMenuToolbarItem.menu = articleThemeMenu
|
||||
articleThemePopUpButton?.menu = articleThemeMenu
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,6 +90,13 @@ extension TimelineViewController {
|
||||
}
|
||||
Browser.open(urlString, inBackground: false)
|
||||
}
|
||||
|
||||
@objc func copyURLFromContextualMenu(_ sender: Any?) {
|
||||
guard let menuItem = sender as? NSMenuItem, let urlString = menuItem.representedObject as? String else {
|
||||
return
|
||||
}
|
||||
URLPasteboardWriter.write(urlString: urlString, to: .general)
|
||||
}
|
||||
|
||||
@objc func performShareServiceFromContextualMenu(_ sender: Any?) {
|
||||
guard let menuItem = sender as? NSMenuItem, let sharingCommandInfo = menuItem.representedObject as? SharingCommandInfo else {
|
||||
@ -168,6 +175,12 @@ private extension TimelineViewController {
|
||||
if articles.count == 1, let link = articles.first!.preferredLink {
|
||||
menu.addSeparatorIfNeeded()
|
||||
menu.addItem(openInBrowserMenuItem(link))
|
||||
menu.addSeparatorIfNeeded()
|
||||
menu.addItem(copyArticleURLMenuItem(link))
|
||||
|
||||
if let externalLink = articles.first?.externalURL, externalLink != link {
|
||||
menu.addItem(copyExternalURLMenuItem(externalLink))
|
||||
}
|
||||
}
|
||||
|
||||
if let sharingMenu = shareMenu(for: articles) {
|
||||
@ -260,6 +273,15 @@ private extension TimelineViewController {
|
||||
|
||||
return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlString)
|
||||
}
|
||||
|
||||
func copyArticleURLMenuItem(_ urlString: String) -> NSMenuItem {
|
||||
return menuItem(NSLocalizedString("Copy Article URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString)
|
||||
}
|
||||
|
||||
func copyExternalURLMenuItem(_ urlString: String) -> NSMenuItem {
|
||||
return menuItem(NSLocalizedString("Copy External URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString)
|
||||
}
|
||||
|
||||
|
||||
func menuItem(_ title: String, _ action: Selector, _ representedObject: Any) -> NSMenuItem {
|
||||
|
||||
|
@ -46,6 +46,11 @@ final class GeneralPreferencesViewController: NSViewController {
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction func showThemesFolder(_ sender: Any) {
|
||||
let url = URL(fileURLWithPath: ArticleThemesManager.shared.folderPath)
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
||||
@IBAction func articleThemePopUpDidChange(_ sender: Any) {
|
||||
guard let menuItem = articleThemePopup.selectedItem else {
|
||||
return
|
||||
|
@ -186,10 +186,16 @@
|
||||
510C418624E5D1B4008226FD /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; };
|
||||
510C43F7243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
|
||||
510C43F8243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
|
||||
510FFAB326EEA22C00F32265 /* ArticleThemesTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
51107746243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */; };
|
||||
51107747243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */; };
|
||||
5110C37D2373A8D100A9C04F /* InspectorIconHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */; };
|
||||
5112435026EE629A002601D2 /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5112434F26EE6291002601D2 /* Sepia.nnwtheme */; };
|
||||
5112435126EE629B002601D2 /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5112434F26EE6291002601D2 /* Sepia.nnwtheme */; };
|
||||
5112435226EE629C002601D2 /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5112434F26EE6291002601D2 /* Sepia.nnwtheme */; };
|
||||
5112435326EE629F002601D2 /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5112434F26EE6291002601D2 /* Sepia.nnwtheme */; };
|
||||
5112435426EE629F002601D2 /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5112434F26EE6291002601D2 /* Sepia.nnwtheme */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5117715524E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
|
||||
5117715624E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
|
||||
@ -595,6 +601,11 @@
|
||||
51CE1C0923621EDA005548FC /* RefreshProgressView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51CE1C0823621EDA005548FC /* RefreshProgressView.xib */; };
|
||||
51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CE1C0A23622006005548FC /* RefreshProgressView.swift */; };
|
||||
51CE1C712367721A005548FC /* testURLsOfCurrentArticle.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EADB213660A100CF2DE4 /* testURLsOfCurrentArticle.applescript */; };
|
||||
51D0214626ED617100FF2E0F /* core.css in Resources */ = {isa = PBXBuildFile; fileRef = 51D0214526ED617100FF2E0F /* core.css */; };
|
||||
51D0214726ED617100FF2E0F /* core.css in Resources */ = {isa = PBXBuildFile; fileRef = 51D0214526ED617100FF2E0F /* core.css */; };
|
||||
51D0214826ED617100FF2E0F /* core.css in Resources */ = {isa = PBXBuildFile; fileRef = 51D0214526ED617100FF2E0F /* core.css */; };
|
||||
51D0214926ED617100FF2E0F /* core.css in Resources */ = {isa = PBXBuildFile; fileRef = 51D0214526ED617100FF2E0F /* core.css */; };
|
||||
51D0214A26ED617100FF2E0F /* core.css in Resources */ = {isa = PBXBuildFile; fileRef = 51D0214526ED617100FF2E0F /* core.css */; };
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */; };
|
||||
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
|
||||
@ -1614,10 +1625,12 @@
|
||||
510C416624E5CDE3008226FD /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
510C418724E5D2E3008226FD /* NetNewsWire_shareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_shareextension_target.xcconfig; sourceTree = "<group>"; };
|
||||
510C43F6243D035C009F70C3 /* ExtensionPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPoint.swift; sourceTree = "<group>"; };
|
||||
510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemesTableViewController.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointPreferencesViewController.swift; sourceTree = "<group>"; };
|
||||
5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorIconHeaderView.swift; sourceTree = "<group>"; };
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
5112434F26EE6291002601D2 /* Sepia.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Sepia.nnwtheme; sourceTree = "<group>"; };
|
||||
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
511B9805237DCAC90028BCAA /* UserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoKey.swift; sourceTree = "<group>"; };
|
||||
@ -1853,6 +1866,7 @@
|
||||
51CD32C724D2E06C009ABAEF /* Secrets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Secrets; sourceTree = "<group>"; };
|
||||
51CE1C0823621EDA005548FC /* RefreshProgressView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RefreshProgressView.xib; sourceTree = "<group>"; };
|
||||
51CE1C0A23622006005548FC /* RefreshProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshProgressView.swift; sourceTree = "<group>"; };
|
||||
51D0214526ED617100FF2E0F /* core.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = core.css; sourceTree = "<group>"; };
|
||||
51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineDataSource.swift; sourceTree = "<group>"; };
|
||||
51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = "<group>"; };
|
||||
51DC07972552083500A3F79F /* ArticleTextSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleTextSize.swift; sourceTree = "<group>"; };
|
||||
@ -2785,6 +2799,7 @@
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */,
|
||||
519ED47924482AEB007F8E94 /* EnableExtensionPointViewController.swift */,
|
||||
519ED455244828C3007F8E94 /* AddExtensionPointViewController.swift */,
|
||||
510FFAB226EEA22C00F32265 /* ArticleThemesTableViewController.swift */,
|
||||
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */,
|
||||
516A09382360A2AE00EAE89B /* SettingsComboTableViewCell.swift */,
|
||||
516A091D23609A3600EAE89B /* SettingsComboTableViewCell.xib */,
|
||||
@ -3083,6 +3098,7 @@
|
||||
51C452A822650DA100C03939 /* Article Rendering */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51D0214526ED617100FF2E0F /* core.css */,
|
||||
B27EEBDF244D15F2000932E6 /* stylesheet.css */,
|
||||
848362FE2262A30E00DA1D35 /* template.html */,
|
||||
517630032336215100E15FFF /* main.js */,
|
||||
@ -3689,6 +3705,7 @@
|
||||
84C9FC9C2262A1A900D921D6 /* Info.plist */,
|
||||
84BB0F812333426400DED65E /* NetNewsWire.entitlements */,
|
||||
51F805ED24284C1C0022C792 /* NetNewsWire-dev.entitlements */,
|
||||
5112434F26EE6291002601D2 /* Sepia.nnwtheme */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
@ -4378,6 +4395,7 @@
|
||||
51E4995F24A875F300B667CB /* stylesheet.css in Resources */,
|
||||
517B2EE824B3E8FE001AC46C /* main_multiplatform.js in Resources */,
|
||||
51E4997324A8784300B667CB /* DefaultFeeds.opml in Resources */,
|
||||
5112435326EE629F002601D2 /* Sepia.nnwtheme in Resources */,
|
||||
51C0516224A77DF800194D5E /* Assets.xcassets in Resources */,
|
||||
5177475F24B39AD500EB0F74 /* About.rtf in Resources */,
|
||||
51E4996024A875F300B667CB /* template.html in Resources */,
|
||||
@ -4385,6 +4403,7 @@
|
||||
51E4995E24A875F300B667CB /* newsfoot.js in Resources */,
|
||||
517B2EE424B3E8FE001AC46C /* blank.html in Resources */,
|
||||
5177475C24B39AD500EB0F74 /* Credits.rtf in Resources */,
|
||||
51D0214926ED617100FF2E0F /* core.css in Resources */,
|
||||
51E4995D24A875F300B667CB /* main.js in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -4395,10 +4414,12 @@
|
||||
files = (
|
||||
517B2EE324B3E8FE001AC46C /* page.html in Resources */,
|
||||
51E4996424A875F400B667CB /* stylesheet.css in Resources */,
|
||||
5112435426EE629F002601D2 /* Sepia.nnwtheme in Resources */,
|
||||
51E4997524A8784400B667CB /* DefaultFeeds.opml in Resources */,
|
||||
51C0516324A77DF800194D5E /* Assets.xcassets in Resources */,
|
||||
51E4996524A875F400B667CB /* template.html in Resources */,
|
||||
517B2EE924B3E8FE001AC46C /* main_multiplatform.js in Resources */,
|
||||
51D0214A26ED617100FF2E0F /* core.css in Resources */,
|
||||
517B2EE524B3E8FE001AC46C /* blank.html in Resources */,
|
||||
51E4996324A875F400B667CB /* newsfoot.js in Resources */,
|
||||
51E4996224A875F400B667CB /* main.js in Resources */,
|
||||
@ -4435,6 +4456,7 @@
|
||||
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||
65ED4052235DEF6C0081F399 /* template.html in Resources */,
|
||||
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */,
|
||||
5112435126EE629B002601D2 /* Sepia.nnwtheme in Resources */,
|
||||
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */,
|
||||
65ED4057235DEF6C0081F399 /* AccountsDetail.xib in Resources */,
|
||||
65ED4058235DEF6C0081F399 /* main.js in Resources */,
|
||||
@ -4445,6 +4467,7 @@
|
||||
65ED405C235DEF6C0081F399 /* ImportOPMLSheet.xib in Resources */,
|
||||
65ED405D235DEF6C0081F399 /* SidebarKeyboardShortcuts.plist in Resources */,
|
||||
514A89A3244FD63F0085E65D /* AddTwitterFeedSheet.xib in Resources */,
|
||||
51D0214726ED617100FF2E0F /* core.css in Resources */,
|
||||
5103A9F5242258C600410853 /* AccountsAddCloudKit.xib in Resources */,
|
||||
65ED405E235DEF6C0081F399 /* DefaultFeeds.opml in Resources */,
|
||||
51333D3C2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */,
|
||||
@ -4485,6 +4508,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5112435226EE629C002601D2 /* Sepia.nnwtheme in Resources */,
|
||||
517630052336215100E15FFF /* main.js in Resources */,
|
||||
5148F44B2336DB4700F8CD8B /* MasterTimelineTitleView.xib in Resources */,
|
||||
511D43D0231FA62500FB1562 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||
@ -4499,6 +4523,7 @@
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */,
|
||||
1768140B2564BB8300D98635 /* NetNewsWire_iOSwidgetextension_target.xcconfig in Resources */,
|
||||
5103A9B424216A4200410853 /* blank.html in Resources */,
|
||||
51D0214826ED617100FF2E0F /* core.css in Resources */,
|
||||
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
|
||||
51F85BEB22724CB600C787DC /* About.rtf in Resources */,
|
||||
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
|
||||
@ -4532,9 +4557,11 @@
|
||||
848363082262A3DD00DA1D35 /* Main.storyboard in Resources */,
|
||||
84C9FC8F22629E8F00D921D6 /* NetNewsWire.sdef in Resources */,
|
||||
84C9FC7D22629E1200D921D6 /* AccountsDetail.xib in Resources */,
|
||||
5112435026EE629A002601D2 /* Sepia.nnwtheme in Resources */,
|
||||
517630042336215100E15FFF /* main.js in Resources */,
|
||||
65ED40A0235DEFF00081F399 /* container-migration.plist in Resources */,
|
||||
5144EA362279FC3D00D19003 /* AccountsAddLocal.xib in Resources */,
|
||||
51D0214626ED617100FF2E0F /* core.css in Resources */,
|
||||
5142194B2353C1CF00E07E2C /* main_mac.js in Resources */,
|
||||
84C9FC8C22629E8F00D921D6 /* KeyboardShortcuts.html in Resources */,
|
||||
B27EEBF9244D15F3000932E6 /* stylesheet.css in Resources */,
|
||||
@ -5601,6 +5628,7 @@
|
||||
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */,
|
||||
51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */,
|
||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||
510FFAB326EEA22C00F32265 /* ArticleThemesTableViewController.swift in Sources */,
|
||||
51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */,
|
||||
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */,
|
||||
|
137
Shared/Article Rendering/core.css
Normal file
137
Shared/Article Rendering/core.css
Normal file
@ -0,0 +1,137 @@
|
||||
/* This is the activity indicator (currently iOS only) that is used while image zooming */
|
||||
|
||||
.activityIndicatorWrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.activityIndicator {
|
||||
z-index: 1;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* see removeWpSmiley; this rule is kept in case a wp-smiley is encountered without alt text */
|
||||
|
||||
.wp-smiley {
|
||||
height: 1em;
|
||||
max-height: 1em;
|
||||
}
|
||||
|
||||
/* Hide the external link at the bottom of Daring Fireball posts */
|
||||
|
||||
.x-netnewswire-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*Block ads and junk*/
|
||||
|
||||
iframe[src*="feedads"],
|
||||
iframe[src*="doubleclick"],
|
||||
iframe[src*="plusone.google"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
a[href*=".ads."],
|
||||
a[href*="feedads"],
|
||||
a[href*="doubleclick"],
|
||||
a[href*="//ads."],
|
||||
a[href*="api.tweetmeme"],
|
||||
a[href*="delicious.com/post?"],
|
||||
a[href*="digg.com/submit?"],
|
||||
a[href*="google.com/bookmarks/mark?"],
|
||||
a[href*="posterous.com/share?"],
|
||||
a[href*="tumblr.com/share?"],
|
||||
a[href*="linkedin.com/shareArticle?"],
|
||||
a[href*="facebook.com/share.php?"],
|
||||
a[href*="http://twitter.com/home?"],
|
||||
a[href*="addtoany.com/share_save"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
img[src*=".ads."],
|
||||
img[src*="//ads."],
|
||||
img[src*="doubleclick"],
|
||||
img[src*="feedads"],
|
||||
img[src*="feedburner"],
|
||||
img[src*="feedblitz"],
|
||||
img[src*="share-buttons"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Newsfoot specific styles. Structural styles come first, theme styles second */
|
||||
|
||||
.newsfoot-footnote-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover {
|
||||
position: absolute;
|
||||
display: block;
|
||||
padding: 0em 1em;
|
||||
margin: 1em;
|
||||
top: 0.75em;
|
||||
max-width: none;
|
||||
border-radius: 0.3em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover {
|
||||
left: calc(-1 * (50vw - 1em));
|
||||
right: calc(-1 * (50vw - 1em));
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-arrow {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 1em;
|
||||
position: absolute;
|
||||
top: -0.5em;
|
||||
left: calc(50% - 0.5em);
|
||||
height: 1em !important;
|
||||
transform: rotate(45deg);
|
||||
z-index:0;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner {
|
||||
border-radius: calc(0.3em - 1px);
|
||||
padding: 1em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.newsfoot-footnote-popover-inner :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover .reversefootnote,
|
||||
.newsfoot-footnote-popover .footnoteBackLink,
|
||||
.newsfoot-footnote-popover .footnote-return,
|
||||
.newsfoot-footnote-popover a[href*='#fn'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
sup[id^='fn'] {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
a.footnote {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
padding: 0.05em 0.75em;
|
||||
border-radius: 1em;
|
||||
min-width: 1em;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
line-height: 1em;
|
||||
position:relative;
|
||||
top: -0.1em;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
/* Shared iOS and macOS CSS rules. Platform specific rules are at the bottom of this file. */
|
||||
|
||||
body {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@ -9,12 +11,15 @@ body {
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.feedlink {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.headerTable {
|
||||
width: 100%;
|
||||
height: 68px;
|
||||
@ -56,9 +61,11 @@ body .headerTable {
|
||||
border-bottom: 1px solid var(--header-table-border-color);
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
body .header {
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
body .header a:link, .header a:visited {
|
||||
color: var(--header-color);
|
||||
}
|
||||
@ -82,9 +89,11 @@ body > .systemMessage {
|
||||
.feedIcon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.rightAlign {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.leftAlign {
|
||||
text-align: start;
|
||||
}
|
||||
@ -160,6 +169,7 @@ pre code {
|
||||
.nnw-overflow {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Instead of the last-child bits, border-collapse: collapse
|
||||
could have been used. However, then the inter-cell borders
|
||||
@ -171,10 +181,12 @@ pre code {
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.nnw-overflow table table {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.nnw-overflow td, .nnw-overflow th {
|
||||
-webkit-hyphens: none;
|
||||
word-break: normal;
|
||||
@ -191,10 +203,12 @@ pre code {
|
||||
.nnw-overflow :matches(thead, tbody, tfoot):last-child > tr:last-child :matches(td, th) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.nnw-overflow td pre {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nnw-overflow table[border="0"] {
|
||||
border-width: 0;
|
||||
}
|
||||
@ -267,19 +281,6 @@ blockquote {
|
||||
border-top: 1px solid var(--header-table-border-color);
|
||||
}
|
||||
|
||||
/* Hide the external link at the bottom of Daring Fireball posts */
|
||||
|
||||
.x-netnewswire-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* see removeWpSmiley; this rule is kept in case a wp-smiley is encountered without alt text */
|
||||
|
||||
.wp-smiley {
|
||||
height: 1em;
|
||||
max-height: 1em;
|
||||
}
|
||||
|
||||
/* Twitter */
|
||||
|
||||
.twitterAvatar {
|
||||
@ -304,126 +305,23 @@ blockquote {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
/*Block ads and junk*/
|
||||
|
||||
iframe[src*="feedads"],
|
||||
iframe[src*="doubleclick"],
|
||||
iframe[src*="plusone.google"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
a[href*=".ads."],
|
||||
a[href*="feedads"],
|
||||
a[href*="doubleclick"],
|
||||
a[href*="//ads."],
|
||||
a[href*="api.tweetmeme"],
|
||||
a[href*="delicious.com/post?"],
|
||||
a[href*="digg.com/submit?"],
|
||||
a[href*="google.com/bookmarks/mark?"],
|
||||
a[href*="posterous.com/share?"],
|
||||
a[href*="tumblr.com/share?"],
|
||||
a[href*="linkedin.com/shareArticle?"],
|
||||
a[href*="facebook.com/share.php?"],
|
||||
a[href*="http://twitter.com/home?"],
|
||||
a[href*="addtoany.com/share_save"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
img[src*=".ads."],
|
||||
img[src*="//ads."],
|
||||
img[src*="doubleclick"],
|
||||
img[src*="feedads"],
|
||||
img[src*="feedburner"],
|
||||
img[src*="feedblitz"],
|
||||
img[src*="share-buttons"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Newsfoot specific styles. Structural styles come first, theme styles second */
|
||||
.newsfoot-footnote-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover {
|
||||
position: absolute;
|
||||
display: block;
|
||||
padding: 0em 1em;
|
||||
margin: 1em;
|
||||
top: 0.75em;
|
||||
max-width: none;
|
||||
border-radius: 0.3em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover {
|
||||
left: calc(-1 * (50vw - 1em));
|
||||
right: calc(-1 * (50vw - 1em));
|
||||
}
|
||||
.newsfoot-footnote-popover-arrow {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 1em;
|
||||
position: absolute;
|
||||
top: -0.5em;
|
||||
left: calc(50% - 0.5em);
|
||||
height: 1em !important;
|
||||
transform: rotate(45deg);
|
||||
z-index:0;
|
||||
}
|
||||
.newsfoot-footnote-popover-inner {
|
||||
border-radius: calc(0.3em - 1px);
|
||||
padding: 1em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.newsfoot-footnote-popover-inner :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover .reversefootnote,
|
||||
.newsfoot-footnote-popover .footnoteBackLink,
|
||||
.newsfoot-footnote-popover .footnote-return,
|
||||
.newsfoot-footnote-popover a[href*='#fn'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
sup[id^='fn'] {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
a.footnote {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
padding: 0.05em 0.75em;
|
||||
border-radius: 1em;
|
||||
min-width: 1em;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
line-height: 1em;
|
||||
position:relative;
|
||||
top: -0.1em;
|
||||
}
|
||||
|
||||
/* light / default */
|
||||
/* Newsfoot theme for light mode (default) */
|
||||
.newsfoot-footnote-popover {
|
||||
background: #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), 0 3px 6px rgba(0, 0, 0, 0.25);
|
||||
color: black;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-arrow {
|
||||
background: #fafafa;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
body a.footnote,
|
||||
body a.footnote:visited,
|
||||
.newsfoot-footnote-popover + a.footnote:hover {
|
||||
@ -431,25 +329,29 @@ body a.footnote:visited,
|
||||
color: white;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
a.footnote:hover,
|
||||
.newsfoot-footnote-popover + a.footnote {
|
||||
background: #666;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
/* dark */
|
||||
/* Newsfoot theme for dark mode */
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
.newsfoot-footnote-popover {
|
||||
background: #444;
|
||||
color: rgb(224, 224, 224);
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-arrow {
|
||||
background: #242424;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner {
|
||||
background: #242424;
|
||||
}
|
||||
|
||||
body a.footnote,
|
||||
body a.footnote:visited,
|
||||
.newsfoot-footnote-popover + a.footnote:hover {
|
||||
@ -457,11 +359,13 @@ a.footnote:hover,
|
||||
color: white;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
a.footnote:hover,
|
||||
.newsfoot-footnote-popover + a.footnote {
|
||||
background: #666;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* iOS Specific */
|
||||
@ -481,6 +385,7 @@ a.footnote:hover,
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
font: -apple-system-body;
|
||||
/* The font-size is replaced at runtime by the dynamic type size */
|
||||
font-size: [[font-size]]px;
|
||||
--primary-accent-color: #086AEE;
|
||||
--secondary-accent-color: #086AEE;
|
||||
@ -499,18 +404,16 @@ a.footnote:hover,
|
||||
body a, body a:visited {
|
||||
color: var(--secondary-accent-color);
|
||||
}
|
||||
|
||||
body .header {
|
||||
font: -apple-system-body;
|
||||
font-size: [[font-size]]px;
|
||||
}
|
||||
|
||||
body .header a:link, body .header a:visited {
|
||||
color: var(--primary-accent-color);
|
||||
}
|
||||
|
||||
.avatar img {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
padding: 5px;
|
||||
@ -520,20 +423,6 @@ a.footnote:hover,
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
}
|
||||
|
||||
.activityIndicatorWrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.activityIndicator {
|
||||
z-index: 1;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* macOS Specific */
|
||||
|
@ -43,30 +43,30 @@ struct ArticleTheme: Equatable {
|
||||
self.path = nil;
|
||||
self.info = ["CreatorHomePage": "https://netnewswire.com/", "CreatorName": "Ranchero Software", "Version": "1.0"]
|
||||
|
||||
let cssPath = Bundle.main.path(forResource: "stylesheet", ofType: "css")!
|
||||
css = Self.stringAtPath(cssPath)
|
||||
let corePath = Bundle.main.path(forResource: "core", ofType: "css")!
|
||||
let stylesheetPath = Bundle.main.path(forResource: "stylesheet", ofType: "css")!
|
||||
css = Self.stringAtPath(corePath)! + "\n" + Self.stringAtPath(stylesheetPath)!
|
||||
|
||||
let templatePath = Bundle.main.path(forResource: "template", ofType: "html")!
|
||||
template = Self.stringAtPath(templatePath)
|
||||
template = Self.stringAtPath(templatePath)!
|
||||
}
|
||||
|
||||
init(path: String) {
|
||||
self.path = path
|
||||
|
||||
if FileManager.default.isFolder(atPath: path) {
|
||||
let infoPath = (path as NSString).appendingPathComponent("Info.plist")
|
||||
self.info = NSDictionary(contentsOfFile: infoPath)
|
||||
let infoPath = (path as NSString).appendingPathComponent("Info.plist")
|
||||
self.info = NSDictionary(contentsOfFile: infoPath)
|
||||
|
||||
let cssPath = (path as NSString).appendingPathComponent("stylesheet.css")
|
||||
self.css = Self.stringAtPath(cssPath)
|
||||
|
||||
let templatePath = (path as NSString).appendingPathComponent("template.html")
|
||||
self.template = Self.stringAtPath(templatePath)
|
||||
let corePath = Bundle.main.path(forResource: "core", ofType: "css")!
|
||||
let stylesheetPath = (path as NSString).appendingPathComponent("stylesheet.css")
|
||||
if let stylesheetCSS = Self.stringAtPath(stylesheetPath) {
|
||||
self.css = Self.stringAtPath(corePath)! + "\n" + stylesheetCSS
|
||||
} else {
|
||||
self.css = Self.stringAtPath(path)
|
||||
self.template = nil
|
||||
self.info = nil
|
||||
self.css = nil
|
||||
}
|
||||
|
||||
let templatePath = (path as NSString).appendingPathComponent("template.html")
|
||||
self.template = Self.stringAtPath(templatePath)
|
||||
}
|
||||
|
||||
static func stringAtPath(_ f: String) -> String? {
|
||||
|
@ -6,12 +6,7 @@
|
||||
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public extension Notification.Name {
|
||||
@ -19,10 +14,15 @@ public extension Notification.Name {
|
||||
static let CurrentArticleThemeDidChangeNotification = Notification.Name("CurrentArticleThemeDidChangeNotification")
|
||||
}
|
||||
|
||||
final class ArticleThemesManager {
|
||||
final class ArticleThemesManager: NSObject, NSFilePresenter {
|
||||
|
||||
static var shared: ArticleThemesManager!
|
||||
private let folderPath: String
|
||||
public let folderPath: String
|
||||
|
||||
lazy var presentedItemOperationQueue = OperationQueue.main
|
||||
var presentedItemURL: URL? {
|
||||
return URL(fileURLWithPath: folderPath)
|
||||
}
|
||||
|
||||
var currentThemeName: String {
|
||||
get {
|
||||
@ -50,28 +50,45 @@ final class ArticleThemesManager {
|
||||
|
||||
init(folderPath: String) {
|
||||
self.folderPath = folderPath
|
||||
self.currentTheme = ArticleTheme.defaultTheme
|
||||
|
||||
super.init()
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
assertionFailure("Could not create folder for Themes.")
|
||||
abort()
|
||||
}
|
||||
|
||||
currentTheme = ArticleTheme.defaultTheme
|
||||
|
||||
let themeFilenames = Bundle.main.paths(forResourcesOfType: ArticleTheme.nnwThemeSuffix, inDirectory: nil)
|
||||
let installedStyleSheets = readInstalledStyleSheets() ?? [String: Date]()
|
||||
for themeFilename in themeFilenames {
|
||||
let themeName = ArticleTheme.themeNameForPath(themeFilename)
|
||||
if !installedStyleSheets.keys.contains(themeName) {
|
||||
try? importTheme(filename: themeFilename)
|
||||
}
|
||||
}
|
||||
|
||||
updateThemeNames()
|
||||
updateCurrentTheme()
|
||||
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSApplication.didBecomeActiveNotification, object: nil)
|
||||
#else
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
|
||||
#endif
|
||||
NSFileCoordinator.addFilePresenter(self)
|
||||
}
|
||||
|
||||
func presentedSubitemDidChange(at url: URL) {
|
||||
updateThemeNames()
|
||||
updateCurrentTheme()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
func themeExists(filename: String) -> Bool {
|
||||
let filenameLastPathComponent = (filename as NSString).lastPathComponent
|
||||
let toFilename = (folderPath as NSString).appendingPathComponent(filenameLastPathComponent)
|
||||
return FileManager.default.fileExists(atPath: toFilename)
|
||||
}
|
||||
|
||||
func importTheme(filename: String) throws {
|
||||
let filenameLastPathComponent = (filename as NSString).lastPathComponent
|
||||
let toFilename = (folderPath as NSString).appendingPathComponent(filenameLastPathComponent)
|
||||
@ -81,15 +98,17 @@ final class ArticleThemesManager {
|
||||
}
|
||||
|
||||
try FileManager.default.copyItem(atPath: filename, toPath: toFilename)
|
||||
|
||||
updateThemeNames()
|
||||
|
||||
let themeName = ArticleTheme.themeNameForPath(filename)
|
||||
var installedStyleSheets = readInstalledStyleSheets() ?? [String: Date]()
|
||||
installedStyleSheets[themeName] = Date()
|
||||
writeInstalledStyleSheets(installedStyleSheets)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc dynamic func applicationDidBecomeActive(_ note: Notification) {
|
||||
updateThemeNames()
|
||||
updateCurrentTheme()
|
||||
func deleteTheme(themeName: String) {
|
||||
if let filename = pathForThemeName(themeName, folder: folderPath) {
|
||||
try? FileManager.default.removeItem(atPath: filename)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -154,4 +173,14 @@ private extension ArticleThemesManager {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readInstalledStyleSheets() -> [String: Date]? {
|
||||
let filePath = (folderPath as NSString).appendingPathComponent("InstalledStyleSheets.plist")
|
||||
return NSDictionary(contentsOfFile: filePath) as? [String: Date]
|
||||
}
|
||||
|
||||
func writeInstalledStyleSheets(_ dict: [String: Date]) {
|
||||
let filePath = (folderPath as NSString).appendingPathComponent("InstalledStyleSheets.plist")
|
||||
(dict as NSDictionary).write(toFile: filePath, atomically: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,7 +70,8 @@ class WebViewController: UIViewController {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(currentArticleThemeDidChangeNotification(_:)), name: .CurrentArticleThemeDidChangeNotification, object: nil)
|
||||
|
||||
// Configure the tap zones
|
||||
configureTopShowBarsView()
|
||||
configureBottomShowBarsView()
|
||||
@ -100,6 +101,10 @@ class WebViewController: UIViewController {
|
||||
reloadArticleImage()
|
||||
}
|
||||
|
||||
@objc func currentArticleThemeDidChangeNotification(_ note: Notification) {
|
||||
loadWebView()
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@objc func showBars(_ sender: Any) {
|
||||
|
@ -377,6 +377,17 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: secondaryActions))
|
||||
}
|
||||
|
||||
var copyActions = [UIAction]()
|
||||
if let action = self.copyArticleURLAction(article) {
|
||||
copyActions.append(action)
|
||||
}
|
||||
if let action = self.copyExternalURLAction(article) {
|
||||
copyActions.append(action)
|
||||
}
|
||||
if !copyActions.isEmpty {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: copyActions))
|
||||
}
|
||||
|
||||
if let action = self.openInBrowserAction(article) {
|
||||
menuElements.append(UIMenu(title: "", options: .displayInline, children: [action]))
|
||||
}
|
||||
@ -902,6 +913,25 @@ private extension MasterTimelineViewController {
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func copyArticleURLAction(_ article: Article) -> UIAction? {
|
||||
guard let url = article.preferredURL else { return nil }
|
||||
let title = NSLocalizedString("Copy Article URL", comment: "Copy Article URL")
|
||||
let action = UIAction(title: title, image: AppAssets.copyImage) { action in
|
||||
UIPasteboard.general.url = url
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func copyExternalURLAction(_ article: Article) -> UIAction? {
|
||||
guard let externalURL = article.externalURL, externalURL != article.preferredLink, let url = URL(string: externalURL) else { return nil }
|
||||
let title = NSLocalizedString("Copy External URL", comment: "Copy External URL")
|
||||
let action = UIAction(title: title, image: AppAssets.copyImage) { action in
|
||||
UIPasteboard.general.url = url
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
|
||||
func openInBrowserAction(_ article: Article) -> UIAction? {
|
||||
guard let _ = article.preferredURL else { return nil }
|
||||
|
@ -212,6 +212,8 @@
|
||||
<true/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<false/>
|
||||
<key>UserAgent</key>
|
||||
<string>NetNewsWire (RSS Reader; https://netnewswire.com/)</string>
|
||||
</dict>
|
||||
|
16
iOS/Resources/Sepia.nnwtheme/Info.plist
Normal file
16
iOS/Resources/Sepia.nnwtheme/Info.plist
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>Sepia</string>
|
||||
<key>ThemeIdentifer</key>
|
||||
<string>com.netnewswire.themes.sepia</string>
|
||||
<key>CreatorHomePage</key>
|
||||
<string>http://netnewswire.com/</string>
|
||||
<key>CreatorName</key>
|
||||
<string>Ranchero Software</string>
|
||||
<key>Version</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
405
iOS/Resources/Sepia.nnwtheme/stylesheet.css
Normal file
405
iOS/Resources/Sepia.nnwtheme/stylesheet.css
Normal file
@ -0,0 +1,405 @@
|
||||
/* Shared iOS and macOS CSS rules. Platform specific rules are at the bottom of this file. */
|
||||
|
||||
body {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
word-wrap: break-word;
|
||||
max-width: 44em;
|
||||
background-color: #FBF0D9;
|
||||
color: #704214;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.feedlink {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.headerTable {
|
||||
width: 100%;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
.systemMessage {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translateX(-55%) translateY(-50%);
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:root {
|
||||
--header-table-border-color: rgba(0, 0, 0, 0.3);
|
||||
--header-color: rgba(0, 0, 0, 0.5);
|
||||
--body-code-color: #704214;
|
||||
--system-message-color: #704214;
|
||||
--feedlink-color: rgba(255, 0, 0, 0.6);
|
||||
--article-title-color: #704214;
|
||||
--article-date-color: rgba(0, 0, 0, 0.5);
|
||||
--table-cell-border-color: lightgray;
|
||||
--primary-accent-color: #43350E;
|
||||
--secondary-accent-color: #43350E;
|
||||
--block-quote-border-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
body a, body a:visited {
|
||||
text-decoration: underline;
|
||||
color: var(--secondary-accent-color);
|
||||
}
|
||||
|
||||
|
||||
body .headerTable {
|
||||
border-bottom: 1px solid var(--header-table-border-color);
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
body .header {
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
body .header a:link, .header a:visited {
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
body code, body pre {
|
||||
color: var(--body-code-color);
|
||||
}
|
||||
|
||||
body > .systemMessage {
|
||||
color: var(--system-message-color);
|
||||
}
|
||||
|
||||
.feedlink a:link, .feedlink a:visited {
|
||||
color: var(--feedlink-color);
|
||||
}
|
||||
|
||||
.avatar img {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.feedIcon {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.rightAlign {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.leftAlign {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.articleTitle a:link, .articleTitle a:visited {
|
||||
color: var(--article-title-color);
|
||||
margin-top: 26px;
|
||||
}
|
||||
|
||||
.articleDateline {
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.articleDateline a:link, .articleDateline a:visited {
|
||||
color: var(--article-date-color);
|
||||
}
|
||||
|
||||
.articleDatelineTitle {
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.articleDatelineTitle a:link, .articleDatelineTitle a:visited {
|
||||
color: var(--article-title-color);
|
||||
}
|
||||
|
||||
.externalLink {
|
||||
margin-bottom: 5px;
|
||||
font-style: italic;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.articleBody {
|
||||
margin-top: 20px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
line-height: 1.15em;
|
||||
font-weight: bold;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 1.4286em;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family: "SF Mono", Menlo, "Courier New", Courier, monospace;
|
||||
font-size: 1em;
|
||||
-webkit-hyphens: none;
|
||||
}
|
||||
|
||||
pre code {
|
||||
letter-spacing: -.027em;
|
||||
font-size: 0.9375em;
|
||||
}
|
||||
|
||||
.nnw-overflow {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Instead of the last-child bits, border-collapse: collapse
|
||||
could have been used. However, then the inter-cell borders
|
||||
overlap the table border, which looks bad.
|
||||
*/
|
||||
.nnw-overflow table {
|
||||
margin-bottom: 1px;
|
||||
border-spacing: 0;
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.nnw-overflow table table {
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.nnw-overflow td, .nnw-overflow th {
|
||||
-webkit-hyphens: none;
|
||||
word-break: normal;
|
||||
border: 1px solid var(--table-cell-border-color);
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.nnw-overflow tr :matches(td, th):last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.nnw-overflow :matches(thead, tbody, tfoot):last-child > tr:last-child :matches(td, th) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.nnw-overflow td pre {
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nnw-overflow table[border="0"] {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
img, figure, video, div, object {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
iframe {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
iframe.nnw-constrained {
|
||||
max-height: 50vw;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
font-size: 14px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
sup {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
bottom: 0.2rem;
|
||||
}
|
||||
|
||||
sub {
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
top: 0.2rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1.5px solid var(--table-cell-border-color);
|
||||
}
|
||||
|
||||
.iframeWrap {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding-top: 56.25%;
|
||||
}
|
||||
|
||||
.iframeWrap iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 15px;
|
||||
border-inline-start: 3px solid var(--block-quote-border-color);
|
||||
}
|
||||
|
||||
/* Feed Specific */
|
||||
|
||||
.feedbin--article-wrap {
|
||||
border-top: 1px solid var(--header-table-border-color);
|
||||
}
|
||||
|
||||
/* Twitter */
|
||||
|
||||
.twitterAvatar {
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
height: 1.7em;
|
||||
width: 1.7em;
|
||||
}
|
||||
|
||||
.twitterUsername {
|
||||
line-height: 1.2;
|
||||
margin-left: 4px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.twitterScreenName {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
.twitterTimestamp {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
/* Newsfoot theme for light mode (default) */
|
||||
.newsfoot-footnote-popover {
|
||||
background: #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), 0 3px 6px rgba(0, 0, 0, 0.25);
|
||||
color: #704214;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-arrow {
|
||||
background: #FBF0D9;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.newsfoot-footnote-popover-inner {
|
||||
background: #FBF0D9;
|
||||
}
|
||||
|
||||
body a.footnote,
|
||||
body a.footnote:visited,
|
||||
.newsfoot-footnote-popover + a.footnote:hover {
|
||||
background: #aaa;
|
||||
color: white;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
a.footnote:hover,
|
||||
.newsfoot-footnote-popover + a.footnote {
|
||||
background: #666;
|
||||
transition: background-color 200ms ease-out;
|
||||
}
|
||||
|
||||
/* iOS Specific */
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
|
||||
body {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
||||
word-break: break-word;
|
||||
-webkit-hyphens: auto;
|
||||
-webkit-text-size-adjust: none;
|
||||
font: Georgia;
|
||||
font-size: [[font-size]]px;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.nnw-overflow table {
|
||||
border: 1px solid var(--secondary-accent-color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* macOS Specific */
|
||||
@supports not (-webkit-touch-callout: none) {
|
||||
|
||||
body {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 64px;
|
||||
padding-left: 48px;
|
||||
padding-right: 48px;
|
||||
font-family: Georgia;
|
||||
}
|
||||
|
||||
.smallText {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mediumText {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.largeText {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.xlargeText {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.xxlargeText {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: 1px solid var(--primary-accent-color);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nnw-overflow table {
|
||||
border: 1px solid var(--primary-accent-color);
|
||||
}
|
||||
|
||||
}
|
15
iOS/Resources/Sepia.nnwtheme/template.html
Normal file
15
iOS/Resources/Sepia.nnwtheme/template.html
Normal file
@ -0,0 +1,15 @@
|
||||
<header class="headerContainer">
|
||||
<table cellpadding=0 cellspacing=0 border=0 class="headerTable">
|
||||
<tr>
|
||||
<td class="header leftAlign"><span class="feedlink">[[feedlink]]</span><br />[[byline]]</td>
|
||||
[[avatars]]
|
||||
</tr>
|
||||
</table>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="articleTitle"><h1>[[title]]</h1></div>
|
||||
<div class="[[dateline_style]]">[[date_medium]]</div>
|
||||
<div class="externalLink">[[external_link]]</div>
|
||||
<div id="bodyContainer" class="articleBody [[text_size_class]]">[[body]]</div>
|
||||
</article>
|
@ -156,6 +156,21 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
private var exceptionArticleFetcher: ArticleFetcher?
|
||||
private(set) var timelineFeed: Feed?
|
||||
|
||||
// We have to defer the selecting of the feed and article due to a behavior (bug?) in iOS 15.
|
||||
// iOS 15 will crash if you are in landscape on an iPad and are restoring article state. We
|
||||
// have no idea why this is, but it happens when you do a select on a UITableView right before
|
||||
// doing a diffable datasource apply.
|
||||
//
|
||||
// Steps to recreate:
|
||||
//
|
||||
// * Try to relaunch the app in the sim.
|
||||
// * Press the Stop button in Xcode
|
||||
// * Wait for all the app suspension activities to complete (widget data, etc)
|
||||
// * Once the article has loaded, navigate to the iPad home screen
|
||||
// * While in landscape, select a feed and then select an article
|
||||
// * Install a fresh build of NNW to an iPad simulator (11 or 12.9' will do) running iPadOS 15
|
||||
private var deferredFeedAndArticleSelect: (feedIndexPath: IndexPath, articleID: String)?
|
||||
|
||||
var timelineMiddleIndexPath: IndexPath?
|
||||
|
||||
private(set) var showFeedNames = ShowFeedName.none
|
||||
@ -437,6 +452,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
}
|
||||
rebuildBackingStores(initialLoad: true)
|
||||
treeControllerDelegate.resetFilterExceptions()
|
||||
|
||||
if let (feedIndexPath, articleID) = deferredFeedAndArticleSelect {
|
||||
selectFeed(indexPath: feedIndexPath) {
|
||||
self.selectArticleInCurrentFeed(articleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
@ -1261,6 +1282,72 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
self.selectArticle(article)
|
||||
}
|
||||
}
|
||||
|
||||
func importTheme(filename: String) {
|
||||
let theme = ArticleTheme(path: filename)
|
||||
|
||||
let localizedTitleText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text")
|
||||
let title = NSString.localizedStringWithFormat(localizedTitleText as NSString, theme.name, theme.creatorName) as String
|
||||
|
||||
let localizedMessageText = NSLocalizedString("Author's Website:\n%@", comment: "Authors website")
|
||||
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.creatorHomePage) as String
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alertController.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
if let url = URL(string: theme.creatorHomePage) {
|
||||
let visitSiteTitle = NSLocalizedString("Show Website", comment: "Show Website")
|
||||
let visitSiteAction = UIAlertAction(title: visitSiteTitle, style: .default) { [weak self] action in
|
||||
UIApplication.shared.open(url)
|
||||
self?.importTheme(filename: filename)
|
||||
}
|
||||
alertController.addAction(visitSiteAction)
|
||||
}
|
||||
|
||||
func importTheme() {
|
||||
do {
|
||||
try ArticleThemesManager.shared.importTheme(filename: filename)
|
||||
confirmImportSuccess(themeName: theme.name)
|
||||
} catch {
|
||||
rootSplitViewController.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
let installThemeTitle = NSLocalizedString("Install Theme", comment: "Install Theme")
|
||||
let installThemeAction = UIAlertAction(title: installThemeTitle, style: .default) { [weak self] action in
|
||||
|
||||
if ArticleThemesManager.shared.themeExists(filename: filename) {
|
||||
let title = NSLocalizedString("Duplicate Theme", comment: "Duplicate Theme")
|
||||
let localizedMessageText = NSLocalizedString("The theme “%@” already exists. Overwrite it?", comment: "Overwrite theme")
|
||||
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name) as String
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alertController.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
let overwriteAction = UIAlertAction(title: NSLocalizedString("Overwrite", comment: "Overwrite"), style: .default) { action in
|
||||
importTheme()
|
||||
}
|
||||
alertController.addAction(overwriteAction)
|
||||
alertController.preferredAction = overwriteAction
|
||||
|
||||
self?.rootSplitViewController.present(alertController, animated: true)
|
||||
} else {
|
||||
importTheme()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
alertController.addAction(installThemeAction)
|
||||
alertController.preferredAction = installThemeAction
|
||||
|
||||
rootSplitViewController.present(alertController, animated: true)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: UISplitViewControllerDelegate
|
||||
@ -2269,12 +2356,24 @@ private extension SceneCoordinator {
|
||||
|
||||
func selectFeedAndArticle(feedNode: Node, articleID: String) -> Bool {
|
||||
if let feedIndexPath = indexPathFor(feedNode) {
|
||||
selectFeed(indexPath: feedIndexPath) {
|
||||
self.selectArticleInCurrentFeed(articleID)
|
||||
}
|
||||
deferredFeedAndArticleSelect = (feedIndexPath, articleID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func confirmImportSuccess(themeName: String) {
|
||||
let title = NSLocalizedString("Theme installed", comment: "Theme installed")
|
||||
|
||||
let localizedMessageText = NSLocalizedString("The theme “%@” has been installed.", comment: "Theme installed")
|
||||
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, themeName) as String
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let doneTitle = NSLocalizedString("Done", comment: "Done")
|
||||
alertController.addAction(UIAlertAction(title: doneTitle, style: .default))
|
||||
|
||||
rootSplitViewController.present(alertController, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -162,6 +162,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
let filename = context.url.standardizedFileURL.path
|
||||
if filename.hasSuffix(ArticleTheme.nnwThemeSuffix) {
|
||||
self.coordinator.importTheme(filename: filename)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
94
iOS/Settings/ArticleThemesTableViewController.swift
Normal file
94
iOS/Settings/ArticleThemesTableViewController.swift
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// ArticleThemesTableViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/12/21.
|
||||
// Copyright © 2021 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import UIKit
|
||||
|
||||
class ArticleThemesTableViewController: UITableViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(articleThemeNamesDidChangeNotification(_:)), name: .ArticleThemeNamesDidChangeNotification, object: nil)
|
||||
}
|
||||
|
||||
@objc func articleThemeNamesDidChangeNotification(_ note: Notification) {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return ArticleThemesManager.shared.themeNames.count + 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
|
||||
let themeName: String
|
||||
if indexPath.row == 0 {
|
||||
themeName = ArticleTheme.defaultTheme.name
|
||||
} else {
|
||||
themeName = ArticleThemesManager.shared.themeNames[indexPath.row - 1]
|
||||
}
|
||||
|
||||
cell.textLabel?.text = themeName
|
||||
if themeName == ArticleThemesManager.shared.currentTheme.name {
|
||||
cell.accessoryType = .checkmark
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
guard let cell = tableView.cellForRow(at: indexPath), let themeName = cell.textLabel?.text else { return }
|
||||
ArticleThemesManager.shared.currentThemeName = themeName
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
guard indexPath.row != 0,
|
||||
let cell = tableView.cellForRow(at: indexPath),
|
||||
let themeName = cell.textLabel?.text else { return nil }
|
||||
|
||||
let deleteTitle = NSLocalizedString("Delete", comment: "Delete")
|
||||
let deleteAction = UIContextualAction(style: .normal, title: deleteTitle) { [weak self] (action, view, completion) in
|
||||
let title = NSLocalizedString("Delete Theme?", comment: "Delete Theme")
|
||||
|
||||
let localizedMessageText = NSLocalizedString("Are you sure you want to delete the theme “%@”?.", comment: "Delete Theme Message")
|
||||
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, themeName) as String
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { action in
|
||||
completion(true)
|
||||
}
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
let deleteTitle = NSLocalizedString("Delete", comment: "Delete")
|
||||
let deleteAction = UIAlertAction(title: deleteTitle, style: .destructive) { action in
|
||||
ArticleThemesManager.shared.deleteTheme(themeName: themeName)
|
||||
completion(true)
|
||||
}
|
||||
alertController.addAction(deleteAction)
|
||||
|
||||
self?.present(alertController, animated: true)
|
||||
}
|
||||
|
||||
deleteAction.image = AppAssets.trashImage
|
||||
deleteAction.backgroundColor = UIColor.systemRed
|
||||
|
||||
return UISwipeActionsConfiguration(actions: [deleteAction])
|
||||
}
|
||||
}
|
@ -264,9 +264,41 @@
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Articles" id="TRr-Ew-IvU">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="SXs-NQ-y3U" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="WFP-zj-Pve" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="739.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="WFP-zj-Pve" id="DCX-wc-LSo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="344.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Theme" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bbw-L9-a3X">
|
||||
<rect key="frame" x="20" y="13.5" width="45" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Default" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DUf-DZ-3Nm">
|
||||
<rect key="frame" x="290" y="13.5" width="46.5" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="DUf-DZ-3Nm" firstAttribute="centerY" secondItem="DCX-wc-LSo" secondAttribute="centerY" id="4Bc-RP-0ZF"/>
|
||||
<constraint firstItem="bbw-L9-a3X" firstAttribute="leading" secondItem="DCX-wc-LSo" secondAttribute="leadingMargin" id="KuW-V7-ekH"/>
|
||||
<constraint firstItem="DUf-DZ-3Nm" firstAttribute="trailing" secondItem="DCX-wc-LSo" secondAttribute="trailingMargin" id="dRF-FI-egK"/>
|
||||
<constraint firstItem="bbw-L9-a3X" firstAttribute="centerY" secondItem="DCX-wc-LSo" secondAttribute="centerY" id="vHl-bd-8cN"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="detail" destination="DUf-DZ-3Nm" id="Eak-bS-PpA"/>
|
||||
<outlet property="label" destination="bbw-L9-a3X" id="lGz-7X-px8"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="SXs-NQ-y3U" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="783.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="SXs-NQ-y3U" id="BpI-Hz-KH2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@ -298,7 +330,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="EYf-v1-lNi" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="783.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="827.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EYf-v1-lNi" id="7nz-0Y-HaW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -332,7 +364,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="WR6-xo-ty2" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="827.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="871.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="WR6-xo-ty2" id="zX8-l2-bVH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -377,20 +409,20 @@
|
||||
<tableViewSection headerTitle="Appearance" id="TkH-4v-yhk">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="EvG-yE-gDF" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="921.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="965.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EvG-yE-gDF" id="wBN-zJ-6pN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="344.5" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="356.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Color Palette" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Fp-li-dGP">
|
||||
<rect key="frame" x="20" y="13.5" width="83.5" height="17"/>
|
||||
<rect key="frame" x="8" y="13.5" width="83.5" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Automatic" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="16m-Ns-Y8V">
|
||||
<rect key="frame" x="271" y="13.5" width="65.5" height="17"/>
|
||||
<rect key="frame" x="283" y="13.5" width="65.5" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@ -413,7 +445,7 @@
|
||||
<tableViewSection headerTitle="Help" id="CS8-fJ-ghn">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="uGk-2d-oFc" style="IBUITableViewCellStyleDefault" id="Tle-IV-D40" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1015.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1059.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tle-IV-D40" id="IJD-ZB-8Wm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -430,7 +462,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="6G3-yV-Eyh" style="IBUITableViewCellStyleDefault" id="Tbf-fE-nfx" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1059.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1103.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tbf-fE-nfx" id="beV-vI-g3r">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -447,7 +479,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="NeD-y8-KrM" style="IBUITableViewCellStyleDefault" id="TIX-yK-rC6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1103.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1147.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIX-yK-rC6" id="qr8-EN-Ofg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -464,7 +496,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lfL-bQ-sOp" style="IBUITableViewCellStyleDefault" id="mFn-fE-zqa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1147.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1191.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mFn-fE-zqa" id="jTe-mf-MRj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -481,7 +513,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DDJ-8P-3YY" style="IBUITableViewCellStyleDefault" id="iGs-ze-4gQ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1191.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1235.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iGs-ze-4gQ" id="EqZ-rF-N0l">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -498,7 +530,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DsV-Qv-X4K" style="IBUITableViewCellStyleDefault" id="taJ-sg-wnU" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1235.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1279.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="taJ-sg-wnU" id="axB-si-1KM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -515,7 +547,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="zMz-hU-UYU" style="IBUITableViewCellStyleDefault" id="OXi-cg-ab9" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1279.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1323.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="OXi-cg-ab9" id="npR-a0-9wv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -532,7 +564,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="T7x-zl-6Yf" style="IBUITableViewCellStyleDefault" id="VpI-0o-3Px" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1323.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1367.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VpI-0o-3Px" id="xRH-i4-vne">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
@ -549,7 +581,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="76A-Ng-kfs" style="IBUITableViewCellStyleDefault" id="jK8-tv-hBD" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1367.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="1411.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jK8-tv-hBD" id="I7Q-GQ-u8Y">
|
||||
<rect key="frame" x="0.0" y="0.0" width="356.5" height="44"/>
|
||||
@ -581,6 +613,7 @@
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="articleThemeDetailLabel" destination="DUf-DZ-3Nm" id="QHN-Le-zyw"/>
|
||||
<outlet property="colorPaletteDetailLabel" destination="16m-Ns-Y8V" id="zdj-Ag-ZUw"/>
|
||||
<outlet property="confirmMarkAllAsReadSwitch" destination="UOo-9z-IuL" id="yLZ-Kf-wDt"/>
|
||||
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/>
|
||||
@ -962,10 +995,10 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3071" y="617"/>
|
||||
</scene>
|
||||
<!--Color Palette-->
|
||||
<!--Article Themes-->
|
||||
<scene sceneID="1mT-fg-ezs">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="ColorPaletteTableViewController" title="Color Palette" id="fO4-og-e41" customClass="ColorPaletteTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableViewController storyboardIdentifier="ArticleThemesTableViewController" title="Article Themes" id="fO4-og-e41" customClass="ArticleThemesTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="I8p-P9-C8h">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -1161,6 +1194,45 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1690" y="1512"/>
|
||||
</scene>
|
||||
<!--Color Palette-->
|
||||
<scene sceneID="i2s-Vf-U8q">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="ColorPaletteTableViewController" title="Color Palette" id="4je-jl-or5" customClass="ColorPaletteTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="nt4-zl-48k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="lFx-ni-1UR" style="IBUITableViewCellStyleDefault" id="Dzq-cs-s7M" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="49.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="Dzq-cs-s7M" id="eAg-RP-lRc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="lFx-ni-1UR">
|
||||
<rect key="frame" x="20" y="0.0" width="334" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="4je-jl-or5" id="Dhv-6P-h0J"/>
|
||||
<outlet property="delegate" destination="4je-jl-or5" id="SQg-Zw-nSl"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="yl7-jL-bYF" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4403" y="151"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="accountLocal" width="119" height="102"/>
|
||||
@ -1172,7 +1244,7 @@
|
||||
<color red="0.031372549019607843" green="0.41568627450980394" blue="0.93333333333333335" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
<systemColor name="groupTableViewBackgroundColor">
|
||||
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="labelColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
|
@ -19,18 +19,15 @@ class SettingsViewController: UITableViewController {
|
||||
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
|
||||
@IBOutlet weak var groupByFeedSwitch: UISwitch!
|
||||
@IBOutlet weak var refreshClearsReadArticlesSwitch: UISwitch!
|
||||
@IBOutlet weak var articleThemeDetailLabel: UILabel!
|
||||
@IBOutlet weak var confirmMarkAllAsReadSwitch: UISwitch!
|
||||
@IBOutlet weak var showFullscreenArticlesSwitch: UISwitch!
|
||||
@IBOutlet weak var colorPaletteDetailLabel: UILabel!
|
||||
@IBOutlet weak var openLinksInNetNewsWire: UISwitch!
|
||||
|
||||
|
||||
|
||||
var scrollToArticlesSection = false
|
||||
weak var presentingParentController: UIViewController?
|
||||
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
// This hack mostly works around a bug in static tables with dynamic type. See: https://spin.atomicobject.com/2018/10/15/dynamic-type-static-uitableview/
|
||||
NotificationCenter.default.removeObserver(tableView!, name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
@ -69,6 +66,8 @@ class SettingsViewController: UITableViewController {
|
||||
} else {
|
||||
refreshClearsReadArticlesSwitch.isOn = false
|
||||
}
|
||||
|
||||
articleThemeDetailLabel.text = ArticleThemesManager.shared.currentTheme.name
|
||||
|
||||
if AppDefaults.shared.confirmMarkAllAsRead {
|
||||
confirmMarkAllAsReadSwitch.isOn = true
|
||||
@ -128,7 +127,7 @@ class SettingsViewController: UITableViewController {
|
||||
}
|
||||
return defaultNumberOfRows
|
||||
case 5:
|
||||
return traitCollection.userInterfaceIdiom == .phone ? 3 : 2
|
||||
return traitCollection.userInterfaceIdiom == .phone ? 4 : 3
|
||||
default:
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
@ -229,6 +228,14 @@ class SettingsViewController: UITableViewController {
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 5:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let articleThemes = UIStoryboard.settings.instantiateController(ofType: ArticleThemesTableViewController.self)
|
||||
self.navigationController?.pushViewController(articleThemes, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 6:
|
||||
let colorPalette = UIStoryboard.settings.instantiateController(ofType: ColorPaletteTableViewController.self)
|
||||
self.navigationController?.pushViewController(colorPalette, animated: true)
|
||||
|
Loading…
x
Reference in New Issue
Block a user