Merge branch 'main' into feat-1844-scroll-mark-as-read

This commit is contained in:
everhardt
2021-11-05 22:03:18 +01:00
18 changed files with 804 additions and 151 deletions

View File

@@ -5,7 +5,7 @@ We welcome contributions!
If youd like to contribute:
1. File a ticket describing the bug you want to fix or feature you want to add. Or find an existing ticket.
2. On the Slack group, bring it up on the #work channel for discussion (which may or may not include implementation discussion). **This is very important, because there might be things you need to know before you start work.**
2. On the [Slack group](https://netnewswire.com/slack), bring it up on the #work channel for discussion (which may or may not include implementation discussion). **This is very important, because there might be things you need to know before you start work.**
3. Once approved, then go for it. Write the code, then do a pull request. Well either have comments or well merge it. (We might revise it afterward, of course.)
## Notes
@@ -88,7 +88,7 @@ members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
[homepage]: https://www.contributor-covenant.org/
[version]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/

View File

@@ -155,6 +155,7 @@ final class DetailWebViewController: NSViewController {
@objc func userDefaultsDidChange(_ note: Notification) {
if articleTextSize != AppDefaults.shared.articleTextSize {
articleTextSize = AppDefaults.shared.articleTextSize
reloadHTMLMaintainingScrollPosition()
}
}

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIAppFonts</key>
<array>
<string>AtkinsonHyperlegible-Regular.ttf</string>
</array>
<key>AppGroup</key>
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire-Evergreen</string>
<key>AppIdentifierPrefix</key>

View File

@@ -136,6 +136,9 @@
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5126EE96226CB48A00C22AFC /* SceneCoordinator.swift */; };
5127B238222B4849006D641D /* DetailKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */; };
5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
512955E72730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 512955E62730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf */; };
512955E82730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 512955E62730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf */; };
512955E92730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 512955E62730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf */; };
512AF9C2236ED52C0066F8BE /* ImageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512AF9C1236ED52C0066F8BE /* ImageHeaderView.swift */; };
512AF9DD236F05230066F8BE /* InteractiveLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512AF9DC236F05230066F8BE /* InteractiveLabel.swift */; };
512D554423C804DE0023FFFA /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */; };
@@ -168,6 +171,9 @@
5137C2E526F3F52D009EFEDB /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5137C2E326F3F52D009EFEDB /* Sepia.nnwtheme */; };
5137C2E626F3F52D009EFEDB /* Sepia.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 5137C2E326F3F52D009EFEDB /* Sepia.nnwtheme */; };
5137C2EA26F63AE6009EFEDB /* ArticleThemeImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5137C2E926F63AE6009EFEDB /* ArticleThemeImporter.swift */; };
51384DED2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 51384DEC2730CECC00C7997D /* Hyperlegible.nnwtheme */; };
51384DEE2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 51384DEC2730CECC00C7997D /* Hyperlegible.nnwtheme */; };
51384DEF2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */ = {isa = PBXBuildFile; fileRef = 51384DEC2730CECC00C7997D /* Hyperlegible.nnwtheme */; };
51386A8E25673277005F3762 /* AccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51386A8D25673276005F3762 /* AccountCell.swift */; };
51386A8F25673277005F3762 /* AccountCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51386A8D25673276005F3762 /* AccountCell.swift */; };
5138E93A24D33E5600AFF0FE /* RSTree in Frameworks */ = {isa = PBXBuildFile; productRef = 5138E93924D33E5600AFF0FE /* RSTree */; };
@@ -1189,6 +1195,7 @@
5126EE96226CB48A00C22AFC /* SceneCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneCoordinator.swift; sourceTree = "<group>"; };
5127B236222B4849006D641D /* DetailKeyboardDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailKeyboardDelegate.swift; sourceTree = "<group>"; };
5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DetailKeyboardShortcuts.plist; sourceTree = "<group>"; };
512955E62730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "AtkinsonHyperlegible-Regular.ttf"; sourceTree = "<group>"; };
512AF9C1236ED52C0066F8BE /* ImageHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeaderView.swift; sourceTree = "<group>"; };
512AF9DC236F05230066F8BE /* InteractiveLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveLabel.swift; sourceTree = "<group>"; };
512D554323C804DE0023FFFA /* OpenInSafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenInSafariActivity.swift; sourceTree = "<group>"; };
@@ -1207,6 +1214,7 @@
51333D3A2468615D00EB5C91 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/AddRedditFeedSheet.xib; sourceTree = SOURCE_ROOT; };
5137C2E326F3F52D009EFEDB /* Sepia.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Sepia.nnwtheme; sourceTree = "<group>"; };
5137C2E926F63AE6009EFEDB /* ArticleThemeImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleThemeImporter.swift; sourceTree = "<group>"; };
51384DEC2730CECC00C7997D /* Hyperlegible.nnwtheme */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Hyperlegible.nnwtheme; sourceTree = "<group>"; };
51386A8D25673276005F3762 /* AccountCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCell.swift; sourceTree = "<group>"; };
513C5CE6232571C2003D4054 /* NetNewsWire iOS Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "NetNewsWire iOS Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
513C5CE8232571C2003D4054 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
@@ -1856,13 +1864,15 @@
511D43CE231FA51100FB1562 /* Resources */ = {
isa = PBXGroup;
children = (
51DEE81126FB9233006DAA56 /* Appanoose.nnwtheme */,
51384DEC2730CECC00C7997D /* Hyperlegible.nnwtheme */,
51DEE81726FBFF84006DAA56 /* Promenade.nnwtheme */,
5137C2E326F3F52D009EFEDB /* Sepia.nnwtheme */,
5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */,
844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */,
844B5B681FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist */,
845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */,
5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */,
51DEE81126FB9233006DAA56 /* Appanoose.nnwtheme */,
51DEE81726FBFF84006DAA56 /* Promenade.nnwtheme */,
5137C2E326F3F52D009EFEDB /* Sepia.nnwtheme */,
512955E62730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf */,
);
path = Resources;
sourceTree = "<group>";
@@ -3364,6 +3374,7 @@
65ED404F235DEF6C0081F399 /* GlobalKeyboardShortcuts.plist in Resources */,
65ED4050235DEF6C0081F399 /* DetailKeyboardShortcuts.plist in Resources */,
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */,
512955E82730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */,
65ED4052235DEF6C0081F399 /* template.html in Resources */,
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */,
5137C2E526F3F52D009EFEDB /* Sepia.nnwtheme in Resources */,
@@ -3379,6 +3390,7 @@
65ED405D235DEF6C0081F399 /* SidebarKeyboardShortcuts.plist in Resources */,
514A89A3244FD63F0085E65D /* AddTwitterFeedSheet.xib in Resources */,
51D0214726ED617100FF2E0F /* core.css in Resources */,
51384DEE2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */,
5103A9F5242258C600410853 /* AccountsAddCloudKit.xib in Resources */,
65ED405E235DEF6C0081F399 /* DefaultFeeds.opml in Resources */,
51333D3C2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */,
@@ -3429,6 +3441,7 @@
511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */,
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
51BB7C312335ACDE008E8144 /* page.html in Resources */,
512955E92730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */,
512392C324E3451400F11704 /* TwitterAdd.storyboard in Resources */,
516A093723609A3600EAE89B /* SettingsComboTableViewCell.xib in Resources */,
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
@@ -3442,6 +3455,7 @@
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */,
516A09402361240900EAE89B /* Account.storyboard in Resources */,
51384DEF2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */,
51C452AB22650DC600C03939 /* template.html in Resources */,
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
@@ -3473,6 +3487,7 @@
84C9FC7D22629E1200D921D6 /* AccountsDetail.xib in Resources */,
5137C2E426F3F52D009EFEDB /* Sepia.nnwtheme in Resources */,
517630042336215100E15FFF /* main.js in Resources */,
512955E72730BDF10041B863 /* AtkinsonHyperlegible-Regular.ttf in Resources */,
65ED40A0235DEFF00081F399 /* container-migration.plist in Resources */,
5144EA362279FC3D00D19003 /* AccountsAddLocal.xib in Resources */,
51D0214626ED617100FF2E0F /* core.css in Resources */,
@@ -3499,6 +3514,7 @@
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */,
51333D3B2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */,
BDCB516724282C8A00102A80 /* AccountsNewsBlur.xib in Resources */,
51384DED2730CECC00C7997D /* Hyperlegible.nnwtheme in Resources */,
514A89A2244FD63F0085E65D /* AddTwitterFeedSheet.xib in Resources */,
5103A9982421643300410853 /* blank.html in Resources */,
515A516E243E7F950089E588 /* ExtensionPointDetail.xib in Resources */,
@@ -3744,7 +3760,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "xcrun -sdk macosx swiftc -target x86_64-macosx10.11 buildscripts/VerifyNoBuildSettings.swift -o $CONFIGURATION_TEMP_DIR/VerifyNoBS\n$CONFIGURATION_TEMP_DIR/VerifyNoBS ${PROJECT_NAME}.xcodeproj/project.pbxproj\n\n\nif [ $? -ne 0 ]\nthen\n echo \"error: Build Setting were found in the project.pbxproj file. Most likely you didn't intend to change this file and should revert it.\"\n exit 1\nfi\n";
shellScript = "xcrun -sdk macosx swift buildscripts/VerifyNoBS.swift --xcode ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj/project.pbxproj\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -4726,8 +4742,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 65ED40F2235DF5E00081F399 /* NetNewsWire_macapp_target_macappstore.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
PRODUCT_NAME = NetNewsWire;
};
name = Debug;
};
@@ -4735,8 +4749,6 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 65ED40F2235DF5E00081F399 /* NetNewsWire_macapp_target_macappstore.xcconfig */;
buildSettings = {
CODE_SIGN_IDENTITY = "Apple Development";
PRODUCT_NAME = NetNewsWire;
};
name = Release;
};

View File

@@ -1,8 +1,8 @@
# ![Icon](Technotes/Images/icon.png) NetNewsWire
Its a free and open source feed reader for macOS and iOS.
Its a free and open-source feed reader for macOS and iOS.
It supports [RSS](http://cyber.harvard.edu/rss/rss.html), [Atom](https://tools.ietf.org/html/rfc4287), [JSON Feed](https://jsonfeed.org/), and [RSS-in-JSON](https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md) formats.
It supports [RSS](https://cyber.harvard.edu/rss/rss.html), [Atom](https://datatracker.ietf.org/doc/html/rfc4287), [JSON Feed](https://jsonfeed.org/), and [RSS-in-JSON](https://github.com/scripting/Scripting-News/blob/master/rss-in-json/README.md) formats.
More info: [https://netnewswire.com/](https://netnewswire.com/)
@@ -16,7 +16,7 @@ Heres [How to Support NetNewsWire](Technotes/HowToSupportNetNewsWire.markdown
[Join the Slack group](https://netnewswire.com/slack) to talk with other NetNewsWire users — and to help out, if youd like to, by testing, coding, writing, providing feedback, or just helping us think things through. Everybody is welcome and encouraged to join.
Every community member is expected to abide by the code of conduct which is included in the [Contributing](CONTRIBUTING.md) page.
Every community member is expected to abide by the [code of conduct](CONTRIBUTING.md#code-of-conduct) which is included in the [Contributing](CONTRIBUTING.md) page.
#### Pull Requests
@@ -33,10 +33,10 @@ git clone https://github.com/Ranchero-Software/NetNewsWire.git
You can locally override the Xcode settings for code signing
by creating a `DeveloperSettings.xcconfig` file locally at the appropriate path.
This allows for a pristine project with code signing set up with the appropriate
developer ID and certificates, and for dev to be able to have local settings
developer ID and certificates, and for developer to be able to have local settings
without needing to check in anything into source control.
You can do this in one of two ways: using the included `setup.sh` script or by creating the folder structure and file manually.
You can do this in one of two ways: using the included `setup.sh` script or by creating the folder structure and file manually.
##### Using `setup.sh`
@@ -46,7 +46,7 @@ You can do this in one of two ways: using the included `setup.sh` script or by c
##### Manually
Make a directory SharedXcodeSettings next to where you have this repository.
Make a directory `SharedXcodeSettings` next to where you have this repository.
The directory structure is:
@@ -54,7 +54,7 @@ The directory structure is:
aDirectory/
SharedXcodeSettings/
DeveloperSettings.xcconfig
NetNewsWire
NetNewsWire/
NetNewsWire.xcworkspace
```
Example:
@@ -90,4 +90,4 @@ functionality disabled. This is because we have API keys that can't be stored i
repository or shared between developers. Certain account types, like Feedly, aren't
enabled and the Reader View isn't enabled because of this.
If you have any problems, we will help you out in Slack (see above).
If you have any problems, we will help you out in Slack ([see above](README.md#Community)).

Binary file not shown.

View 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>Hyperlegible</string>
<key>ThemeIdentifier</key>
<string>com.netnewswire.themes.hyperlegible</string>
<key>CreatorHomePage</key>
<string>http://netnewswire.com/</string>
<key>CreatorName</key>
<string>Ranchero Software</string>
<key>Version</key>
<integer>1</integer>
</dict>
</plist>

View File

@@ -0,0 +1,510 @@
/* Shared iOS and macOS CSS rules. Platform specific rules are at the bottom of this file. */
@font-face
{
font-family: 'Atkinson Hyperlegible';
src: local('AtkinsonHyperlegible-Regular'),url('AtkinsonHyperlegible-Regular.ttf') format('truetype');
}
body {
margin-left: auto;
margin-right: auto;
word-wrap: break-word;
max-width: 44em;
}
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.1);
--header-color: rgba(8, 106, 238, 1);
--body-code-color: #666;
--system-message-color: #cbcbcb;
--feedlink-color: rgba(255, 0, 0, 0.6);
--article-title-color: #333;
--article-date-color: rgba(0, 0, 0, 0.5);
--table-cell-border-color: lightgray;
}
@media(prefers-color-scheme: dark) {
:root {
--header-color: rgba(94, 158, 244, 1);
--body-code-color: #b2b2b2;
--system-message-color: #5f5f5f;
--feedlink-color: rgba(94, 158, 244, 1);
--article-title-color: #e0e0e0;
--article-date-color: rgba(255, 255, 255, 0.8);
--table-cell-border-color: dimgray;
}
}
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: 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 {
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;
}
/* 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 {
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;
font-size: 135%;
word-break: break-word;
-webkit-hyphens: auto;
-webkit-text-size-adjust: none;
}
:root {
color-scheme: light dark;
font: font-family: 'Atkinson Hyperlegible', -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;
--block-quote-border-color: rgba(8, 106, 238, 0.75);
}
@media(prefers-color-scheme: dark) {
:root {
--primary-accent-color: #2D80F1;
--secondary-accent-color: #5E9EF4;
--block-quote-border-color: rgba(94, 158, 244, 0.75);
--header-table-border-color: rgba(255, 255, 255, 0.2);
}
}
body a, body a:visited {
color: var(--secondary-accent-color);
}
body .header {
font: font-family: 'Atkinson Hyperlegible', -apple-system-body;
font-size: 135%;
}
body .header a:link, body .header a:visited {
color: var(--primary-accent-color);
}
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: 'Atkinson Hyperlegible', -apple-system;
}
.smallText {
font-size: 22px;
}
.mediumText {
font-size: 24px;
}
.largeText {
font-size: 26px;
}
.xlargeText {
font-size: 28px;
}
.xxlargeText {
font-size: 30px;
}
:root {
color-scheme: light dark;
--accent-color: rgba(8, 106, 238, 1);
--block-quote-border-color: rgba(8, 106, 238, .50);
}
@media(prefers-color-scheme: dark) {
:root {
--accent-color: rgba(94, 158, 244, 1);
--block-quote-border-color: rgba(94, 158, 244, .50);
--header-table-border-color: rgba(255, 255, 255, 0.1);
}
}
body .header {
font-size: 135%;
}
body .articleTitle {
font-size: 135%;
}
body .articleDateline {
font-size: 135%;
}
body .externalLink {
font-size: 135%;
}
body a, body a:visited {
color: var(--accent-color);
}
pre {
border: 1px solid var(--accent-color);
padding: 10px;
}
.nnw-overflow table {
border: 1px solid var(--accent-color);
}
}

View File

@@ -0,0 +1,47 @@
<!-- Template Variables
title: The title of the article
preferred_link: The best link to associate with the article for linking out.
external_link_label: A localized label for the external link.
external_link_stripped: The external link minus the scheme. Useful for displaying the external link.
external_link: The external link of the article if there is one provided by the author.
feed_link_title: The name of the feed associated with this article.
feed_link: The URL of the feed associated with this article.
byline: HTML that combines all the authors and links to them if available.
avatar_src: The image source URL for the feed icon / avatar.
dateline_style: Either "articleDateline" or "articleDatelineTitle" depending on if the title was populated or not.
datetime_long: Long version of a combined publish date and time.
datetime_medium: Medium length version of a combined publish date and time.
datetime_short: Short version of a combined publish date and time.
date_long: Long version of the publish date.
date_medium: Medium version of the publish date.
date_short: Long version of the publish date.
time_long: Long version of the publish time.
time_medium: Medium version of the publish time.
time_short: Long version of the publish time.
text_size_class: The size class that the user has selected in Preferences for article text.
body: The body of the article.
-->
<header class="headerContainer">
<table cellpadding=0 cellspacing=0 border=0 class="headerTable">
<tr>
<td class="header leftAlign"><a class="feedlink" href="[[feed_link]]">[[feed_link_title]]</a><br />[[byline]]</td>
<td class="header rightAlign avatar"><img id="nnwImageIcon" src="[[avatar_src]]" height=48 width=48 /></td>
</tr>
</table>
</header>
<article>
<div class="articleTitle"><h1><a href="[[preferred_link]]">[[title]]</a></h1></div>
<div class="[[dateline_style]]"><a href="[[preferred_link]]">[[datetime_medium]]</a></div>
<div class="externalLink">[[external_link_label]] <a href="[[external_link]]">[[external_link_stripped]]</a></div>
<div id="bodyContainer" class="articleBody [[text_size_class]]">[[body]]</div>
</article>

View File

@@ -1,11 +1,11 @@
# Accessibility
Millions of Mac users have some disability or special needs. They use screen readers and special
hardware to open up a world that they would otherwise be cut off from. With a small amount of
hardware to open up a world that they would otherwise be cut off from. With a small amount of
developer work, we can help these users live better lives.
Because NetNewsWire utilizes standard AppKit controls and views, accessibility is already built in.
However this is only a starting point. Any customized controls and views will have accessibility
However, this is only a starting point. Any customized controls and views will have accessibility
work and the application as a whole has to be tested to make sure users can operate if efficiently.
This document lays the groundwork to ensure that NetNewsWire has first class accessibility features.

View File

@@ -14,7 +14,7 @@ This separation is deliberate. There are two main reasons: syncing, and strange
When syncing with another service, its entirely likely that the service will report article status information in calls that are separate from calls to retrieve articles.
Thus the app might learn about statuses for articles it hasnt seen yet.
Thus, the app might learn about statuses for articles it hasnt seen yet.
This way the app can store those statuses without having to have their corresponding articles. And then, when the app does download those articles, it has their statuses already in the database.

View File

@@ -17,15 +17,15 @@ That is Three-Flow applied to NetNewsWire. It would be that simple, but we have
Today (6/12/2019) we have 2 branches, main and macOS Candidate, in the main repository which will eventually grow to be 5 branches.
There will also be a number of repository forks that NetNewWire developers will create to do bug fixes and implement new features (not shown here). Typically contributers will fork the Main branch to thier own repository. They would then create a feature/bugfix branch on their repository. Once work on thier forked branch is complete, they will submit a pull request to be merged back into the main repository main.
There will also be a number of repository forks that NetNewWire developers will create to do bug fixes and implement new features (not shown here). Typically, contributors will fork the Main branch to their own repository. They would then create a feature/bugfix branch on their repository. Once work on their forked branch is complete, they will submit a pull request to be merged back into the main repository main.
## Tagging
Each release should be tagged using [Semantic Versioning](https://semver.org/). Candidates will continue to be tagged using the current convention which denotes the difference between developer, alpha and beta releases. Additionally, we will need to use a convention to avoid tag name collisions between iOS and macOS products. macOS releases will be suffixed with "mac-" and iOS releases will be suffixed with "ios-". (See the above diagram for examples.)
Each release should be tagged using [Semantic Versioning](https://semver.org/). Candidates will continue to be tagged using the current convention which denotes the difference between developer, alpha and beta releases. Additionally, we will need to use a convention to avoid tag name collisions between iOS and macOS products. macOS releases will be suffixed with "mac-" and iOS releases will be suffixed with "ios-". (See the above diagram for examples.)
## Packages
NetNewsWire uses Swift Packages to manage project dependencies. All the packages are under the same project umbrella as NetNewWire and there are no third party dependencies to manage. These packages are mostly stable at this point. For simplicity sake, all development on the packages will continue on their repository Main branch. These packages wont be managed as separate projects with separate releases/tags at this time.
NetNewsWire uses Swift Packages to manage project dependencies. All the packages are under the same project umbrella as NetNewWire and there are no third-party dependencies to manage. These packages are mostly stable at this point. For simplicitys sake, all development on the packages will continue on their repository Main branch. These packages wont be managed as separate projects with separate releases/tags at this time.
## Summary

View File

@@ -5,39 +5,26 @@ First thing: dont send money. This app is [written for love](https://inessent
NetNewsWire is all about three things:
* The open web
* High-quality open source Mac and iOS apps
* High-quality open-source Mac and iOS apps
* The community that loves both of the above
Supporting all these things takes *work*.
### Here are some things you can do
In no particular order…
In no particular order
Write a blog instead of posting to Twitter or Facebook. (You can always re-post to those places if you want to extend your reach.) [Micro.blog](https://micro.blog/) is one good place to get going, but its not the only one.
Use an RSS reader even if its not NetNewsWire. (There are a bunch of good ones!)
Teach other people to use RSS readers. Blog about RSS readers. And about other open web technologies and apps.
Suggest apps for [macopenweb.com](https://macopenweb.com/).
Write Mac and iOS apps that promote use of the open web.
Donate to charities that promote literacy.
Tell other people about cool blogs and feeds youve found.
Support indie podcast apps.
Vote for candidates whose policies are not cruel.
Support your local library.
Be bold and do your best work.
Support indie developers — pay for apps that cost money. Even though NetNewsWire is free, apps are most definitely *not* free to make, and it costs money to keep improving them. Its worth it.
Finally: report bugs and make feature requests on our Issues tracker. You can also join the Slack group — its not just for coders. We also need testers, writers, and, especially, people who are willing to talk things over. Most of software development is just making decisions, and we appreciate all the help we can get!
Or: skip helping us, and, instead, help people who need help more than we do. Those people should not be hard to find.
* Write a blog instead of posting to Twitter or Facebook. (You can always re-post to those places if you want to extend your reach.) [Micro.blog](https://micro.blog/) is one good place to get going, but its not the only one.
* Use an RSS reader even if its not NetNewsWire. (There are a bunch of good ones!)
* Teach other people to use RSS readers. Blog about RSS readers. And about other open web technologies and apps.
* Suggest apps for [macopenweb.com](https://macopenweb.com/).
* Write Mac and iOS apps that promote use of the open web.
* Donate to charities that promote literacy.
* Tell other people about cool blogs and feeds youve found.
* Support indie podcast apps.
* Vote for candidates whose policies are not cruel.
* Support your local library.
* Be bold and do your best work.
* Support indie developers — pay for apps that cost money. Even though NetNewsWire is free, apps are most definitely *not* free to make, and it costs money to keep improving them. Its worth it.
* Finally: report bugs and make feature requests on our Issues tracker. You can also join the Slack group — its not just for coders. We also need testers, writers, and, especially, people who are willing to talk things over. Most of software development is just making decisions, and we appreciate all the help we can get!
* Or: skip helping us, and, instead, help people who need help more than we do. Those people should not be hard to find.

155
buildscripts/VerifyNoBS.swift Executable file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/swift
// This script is meant to be called from an Xcode run script build phase
// It verifies there are no buildSettings embedded in the Xcode project
// as it is preferable to have build settings specified in .xcconfig files
// How to use:
// Put this script in a folder called 'buildscripts' next to your xcode project
// Then, add a Run script build phase to one of your targets with this as the script
//
// xcrun -sdk macosx swift buildscripts/VerifyNoBS.swift --xcode ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj/project.pbxproj
//
import Darwin
import Foundation
/// A message with its file name and location
struct LocatedMessage {
let message: String
let fileUrl: URL
let line: Int
}
/// Utility to process the pbxproj file
struct BuildSettingsVerifier {
public enum ProcessXcodeprojResult {
case foundBuildSettings([LocatedMessage])
case error(String)
case success(String)
}
/// Mode to run the utility in. Mode defines the output format
public enum Mode {
/// Write errors to stderr
case cmd
/// Write errors to stdout in a format that is picked up by Xcode
case xcode
}
/// The mode to run in
let mode: Mode
/// The absolute file URL to the pbxproj file
let projUrl: URL
init(mode: Mode, projUrl: URL) {
self.mode = mode
self.projUrl = projUrl
}
/// Reports an error either to stderr or to stdout, depending on the mode
func reportError(message: String, fileUrl: URL? = nil, line: Int? = nil) {
switch mode {
case .cmd:
let stderr = FileHandle.standardError
if let data = "\(message)\n".data(using: String.Encoding.utf8, allowLossyConversion: false) {
stderr.write(data)
} else {
print("There was an error. Could not convert error message to printable string")
}
case .xcode:
var messageParts = [String]()
if let fileUrl = fileUrl {
messageParts.append("\(fileUrl.path):")
}
if let line = line {
messageParts.append("\(line): ")
}
messageParts.append("error: \(message)")
print(messageParts.joined())
}
}
/// Inspect the pbxproj file for non-empty buildSettings
func processXcodeprojAt(url: URL) -> ProcessXcodeprojResult {
let startTime = Date()
guard let xcodeproj = try? String(contentsOf: url, encoding: String.Encoding.utf8) else {
return .error("Failed to read xcodeproj contents from \(url)")
}
let lines = xcodeproj.components(separatedBy: CharacterSet.newlines)
print("Found \(lines.count) lines")
var locatedMessages: [LocatedMessage] = []
var inBuildSettingsBlock = false
for (lineIndex, nthLine) in lines.enumerated() {
if inBuildSettingsBlock {
if nthLine.range(of: "\\u007d[:space:]*;", options: .regularExpression) != nil {
inBuildSettingsBlock = false
} else if nthLine.range(of: "CODE_SIGN_IDENTITY") != nil {
} else {
let message = mode == .cmd ? " \(nthLine)\n" : "Setting '\(nthLine.trimmingCharacters(in: .whitespacesAndNewlines))' should be in an xcconfig file"
locatedMessages.append(LocatedMessage(
message: message,
fileUrl: url,
line: lineIndex + 1
))
}
} else {
if nthLine.range(of: "buildSettings[:space:]*=", options: .regularExpression) != nil {
inBuildSettingsBlock = true
}
}
}
let timeInterval = Date().timeIntervalSince(startTime)
print("Process took \(timeInterval) seconds")
if locatedMessages.count > 0 {
return .foundBuildSettings(locatedMessages)
}
return .success(":-)")
}
public func verify() -> Int32 {
print("Verifying there are no build settings...")
let result = processXcodeprojAt(url: projUrl)
switch result {
case .error(let str):
reportError(message: "Error verifying build settings: \(str)")
return EXIT_FAILURE
case .foundBuildSettings(let locatedMessages):
reportError(message: "Found build settings in project file")
for msg in locatedMessages {
reportError(message: msg.message, fileUrl: msg.fileUrl, line: msg.line)
}
return EXIT_FAILURE
case .success:
print("No build settings found in project file")
return EXIT_SUCCESS
}
}
}
var commandLineArgs = CommandLine.arguments.dropFirst()
//print("processArgs were \(commandLineArgs)")
if commandLineArgs.count < 1 {
print("Usage: \(#file) [--xcode] /path/to/Project.xcodeproj/project.pbxproj")
exit(EXIT_FAILURE)
} else {
let xcodeProjFilePath = commandLineArgs.removeLast()
let mode: BuildSettingsVerifier.Mode = commandLineArgs.count > 0 && commandLineArgs.last == "--xcode" ? .xcode : .cmd
let myUrl = URL(fileURLWithPath: xcodeProjFilePath)
let verifier = BuildSettingsVerifier(mode: mode, projUrl: myUrl)
let exitCode = verifier.verify()
exit(exitCode)
}

View File

@@ -1,84 +0,0 @@
#!/usr/bin/swift
// This script is originally from github.com/olofhellman/VerifyNoBS
// The idea is that all build settings should be kept in .xcconfig files,
// rather than in the xcode pbxproj file inside an Xcode project bundle
// Having the script run as part of a regular build ensures that the project file
// doesn't accidentally accumulate build settings
import Darwin
import Foundation
func reportError(message: String) {
print("error message was \(message)")
let stderr = FileHandle.standardError
if let data = message.data(using: String.Encoding.utf8, allowLossyConversion: false) {
stderr.write(data)
} else {
print("there was an error. script \"VerifyNoBuildSettings\" could not convert error message to printable string")
}
}
public enum ProcessXcodeprojResult {
case FoundBuildSettings([String])
case Error(String)
case OK(String)
}
public func processXcodeprojAt(url: URL) -> ProcessXcodeprojResult {
let startTime = Date()
guard let xcodeproj = try? String(contentsOf: url, encoding: String.Encoding.utf8) else {
return .Error("script \"VerifyNoBuildSettings\" failed making xcodeproj from url")
}
let lines = xcodeproj.components(separatedBy: CharacterSet.newlines)
print ("found \(lines.count) lines")
var badLines: [String] = []
var inBuildSettingsBlock = false
for nthLine in lines {
if inBuildSettingsBlock {
if let _ = nthLine.range(of:"\\u007d[:space:]*;", options: .regularExpression) {
inBuildSettingsBlock = false
} else if let _ = nthLine.range(of:"CODE_SIGN_IDENTITY") {
} else if let _ = nthLine.range(of:"PRODUCT_NAME") {
} else {
badLines.append(nthLine)
}
} else {
if let _ = nthLine.range(of:"buildSettings[:space:]*=", options: .regularExpression) {
inBuildSettingsBlock = true
}
}
}
let timeInterval = Date().timeIntervalSince(startTime)
print ("process took \(timeInterval) seconds")
if (badLines.count > 0) {
return .FoundBuildSettings(badLines)
}
return .OK(":-)")
}
print("Verifying no buildSettings...")
let commandLineArgs = CommandLine.arguments
print("processArgs were \(commandLineArgs)")
let xcodeprojfilepath = commandLineArgs[1]
let myUrl = URL(fileURLWithPath:xcodeprojfilepath)
let result = processXcodeprojAt(url: myUrl)
switch result {
case .Error(let str):
reportError (message: "error script \"VerifyNoBuildSettings\" encountered an error: \(str)")
exit(EXIT_FAILURE)
case .FoundBuildSettings(let badLines):
reportError (message: "script \"VerifyNoBuildSettings\" found build settings in the project file:")
for badLine in badLines {
reportError (message: " \(badLine)\n")
}
exit(EXIT_FAILURE)
case .OK:
print ("script \"VerifyNoBuildSettings\" verified the project contained no buildSettings")
exit(EXIT_SUCCESS)
}

View File

@@ -119,7 +119,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
#if DEBUG
syncTimer!.update()
#endif
return true
}

View File

@@ -44,6 +44,8 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
override var canBecomeFirstResponder: Bool {
return true
}
private var reloadCoalescingQueue = CoalescingQueue(name: "Reload Visible", interval: 0.5)
override func viewDidLoad() {
@@ -113,16 +115,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
return
}
var node: Node? = nil
if let coordinator = representedObject as? SceneCoordinator, let feed = coordinator.timelineFeed {
node = coordinator.rootNode.descendantNodeRepresentingObject(feed as AnyObject)
} else {
node = coordinator.rootNode.descendantNodeRepresentingObject(representedObject as AnyObject)
}
guard let unreadCountNode = node, let indexPath = coordinator.indexPathFor(unreadCountNode) else { return }
tableView.reloadRows(at: [indexPath], with: .none)
restoreSelectionIfNecessary(adjustScroll: false)
queueReloadAllVisible()
}
@objc func faviconDidBecomeAvailable(_ note: Notification) {
@@ -840,6 +833,14 @@ private extension MasterFeedViewController {
return ""
}
func queueReloadAllVisible() {
reloadCoalescingQueue.add(self, #selector(reloadQueuedAllVisible))
}
@objc func reloadQueuedAllVisible() {
reloadAllVisibleCells()
}
func configureCellsForRepresentedObject(_ representedObject: AnyObject) {
applyToCellsForRepresentedObject(representedObject, configure)
}

View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIAppFonts</key>
<array>
<string>AtkinsonHyperlegible-Regular.ttf</string>
</array>
<key>AppGroup</key>
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire.iOS</string>
<key>AppIdentifierPrefix</key>