Merge branch 'main' into iOS-Custom-notification-tone

This commit is contained in:
Diggory Laycock
2022-10-05 18:29:52 +01:00
committed by GitHub
25 changed files with 404 additions and 232 deletions

View File

@@ -102,7 +102,6 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(reloadDueToThemeChange(_:)), name: .CurrentArticleThemeDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(configureAppearanceMenu(_:)), name: .ArticleThemeNamesDidChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateUnreadCountIndicator(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
@@ -349,9 +348,8 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
/// For iPad, this indicator is visible if it is in `portrait` or `unknown`
/// orientation, **and** the unread count is > 0.
/// - Parameter sender: `Any` (optional)
@objc
public func updateUnreadCountIndicator(_ sender: Any? = nil) {
if UIDevice.current.userInterfaceIdiom == .phone {
public func updateUnreadCountIndicator(forDisplayMode displayMode: UISplitViewController.DisplayMode? = nil) {
func changeUnreadCountIndicator() {
if currentUnreadCount > 0 {
let unreadCountView = MasterTimelineUnreadCountView(frame: .zero)
unreadCountView.unreadCount = currentUnreadCount
@@ -360,17 +358,13 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
} else {
navigationItem.leftBarButtonItem = nil
}
}
if UIDevice.current.userInterfaceIdiom == .phone {
changeUnreadCountIndicator()
} else {
if UIDevice.current.orientation.isPortrait || !UIDevice.current.orientation.isValidInterfaceOrientation {
if currentUnreadCount > 0 {
let unreadCountView = MasterTimelineUnreadCountView(frame: .zero)
unreadCountView.unreadCount = currentUnreadCount
unreadCountView.setFrameIfNotEqual(CGRect(x: 0, y: 0, width: unreadCountView.intrinsicContentSize.width, height: unreadCountView.intrinsicContentSize.height))
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: unreadCountView)
} else {
navigationItem.leftBarButtonItem = nil
}
if displayMode == nil || displayMode == .secondaryOnly {
changeUnreadCountIndicator()
} else {
navigationItem.leftBarButtonItem = nil
}

110
iOS/Resources/About.plist Normal file
View File

@@ -0,0 +1,110 @@
<?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>AppCredits</key>
<array>
<dict>
<key>role</key>
<string>Lead Developer</string>
<key>name</key>
<string>Maurice Parker</string>
<key>url</key>
<string>https://vincode.io</string>
</dict>
<dict>
<key>url</key>
<string>https://twitter.com/BradEllis</string>
<key>name</key>
<string>Brad Ellis</string>
<key>role</key>
<string>App Icon</string>
</dict>
<dict>
<key>url</key>
<string>https://twitter.com/kielgillard</string>
<key>name</key>
<string>Kiel Gillard</string>
<key>role</key>
<string>Feedly Syncing</string>
</dict>
<dict>
<key>url</key>
<string>https://twitter.com/quanganhdo</string>
<key>name</key>
<string>Anh Do</string>
<key>role</key>
<string>NewsBlur Syncing</string>
</dict>
<dict>
<key>url</key>
<string>https://github.com/wevah</string>
<key>name</key>
<string>Nate Weaver</string>
<key>role</key>
<string>Under-the-hood magic and CSS stylins</string>
</dict>
<dict>
<key>url</key>
<string>https://github.com/brehaut/</string>
<key>name</key>
<string>Andrew Brehaut</string>
<key>role</key>
<string>Newsfoot (JS footnote displayer)</string>
</dict>
<dict>
<key>url</key>
<string>https://nostodnayr.net</string>
<key>name</key>
<string>Ryan Dotson</string>
<key>role</key>
<string>Help Book</string>
</dict>
</array>
<key>AdditionalContributors</key>
<array>
<dict>
<key>name</key>
<string>Daniel Jalkut</string>
<key>url</key>
<string>https://github.com/danielpunkass</string>
</dict>
<dict>
<key>name</key>
<string>Joe Heck</string>
<key>url</key>
<string>https://rhonabwy.com</string>
</dict>
<dict>
<key>name</key>
<string>Olof Hellman</string>
<key>url</key>
<string>https://github.com/olofhellman</string>
</dict>
<dict>
<key>name</key>
<string>Rizwan Mohamed Ibrahim</string>
<key>url</key>
<string>https://blog.rizwan.dev/</string>
</dict>
<dict>
<key>name</key>
<string>Stuart Breckenridge</string>
<key>url</key>
<string>https://stuartbreckenridge.net</string>
</dict>
<dict>
<key>name</key>
<string>Phil Viso</string>
<key>url</key>
<string>https://twitter.com/philviso</string>
</dict>
<dict>
<key>name</key>
<string>...and many more</string>
<key>url</key>
<string>https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors</string>
</dict>
</array>
</dict>
</plist>

View File

@@ -1,12 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2513
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red10\green96\blue255;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;\cssrgb\c0\c47843\c100000\cname systemBlueColor;}
\margl1440\margr1440\vieww8340\viewh9300\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li363\fi-364\pardirnatural\partightenfactor0
\f0\b\fs28 \cf2 By Brent Simmons and the Ranchero Software team
\fs22 \
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
{\field{\*\fldinst{HYPERLINK "https://netnewswire.com/"}}{\fldrslt
\fs28 \cf3 netnewswire.com}}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "AppIcon-1024px 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "AppIcon-1024px.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,17 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2513
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
\margl1440\margr1440\vieww14220\viewh13280\viewkind0
\deftab720
\pard\pardeftab720\sa60\partightenfactor0
\f0\fs22 \cf2 Lead developer: {\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}\
App icon: {\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\
Feedly syncing: {\field{\*\fldinst{HYPERLINK "https://twitter.com/kielgillard"}}{\fldrslt Kiel Gillard}}\
NewsBlur syncing: {\field{\*\fldinst{HYPERLINK "https://twitter.com/quanganhdo"}}{\fldrslt Anh Do}}\
Under-the-hood magic and CSS stylin\'92s: {\field{\*\fldinst{HYPERLINK "https://github.com/wevah"}}{\fldrslt Nate Weaver}}\
Newsfoot (JS footnote displayer): {\field{\*\fldinst{HYPERLINK "https://github.com/brehaut/"}}{\fldrslt Andrew Brehaut}}\
Help book: {\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\
And featuring contributions from {\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut}}, {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"}}{\fldrslt Joe Heck}}, {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://blog.rizwan.dev/"}}{\fldrslt Rizwan Mohamed Ibrahim}}, {\field{\*\fldinst{HYPERLINK "https://mynameisstuart.com/"}}{\fldrslt Stuart Breckenridge}}, {\field{\*\fldinst{HYPERLINK "https://twitter.com/philviso"}}{\fldrslt Phil Viso}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors"}}{\fldrslt many more}}!\
}

View File

@@ -1,9 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2513
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\sa60\partightenfactor0
\f0\fs22 \cf2 NetNewsWire 6 is dedicated to everyone working to save democracy around the world.}

5
iOS/Resources/Thanks.md Normal file
View File

@@ -0,0 +1,5 @@
Thanks to Sheila and my family; thanks to my friends in Seattle and around the globe; thanks to the ever-patient and ever-awesome NetNewsWire beta testers.
Thanks to [Gus Mueller](https://shapeof.com/) for [FMDB](https://github.com/ccgus/fmdb) by [Flying Meat Software](http://flyingmeat.com/). Thanks to [GitHub](https://github.com) and [Slack](https://slack.com) for making open source collaboration easy and fun. Thanks to [Ben Ubois](https://benubois.com/) at [Feedbin](https://feedbin.com/) for all the extra help with syncing and article rendering — and for [hosting the server for the Reader view](https://feedbin.com/blog/2019/03/11/the-future-of-full-content/).
NetNewsWire 6 is dedicated to everyone working to save democracy around the world.

View File

@@ -1,11 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2511
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
\margl1440\margr1440\vieww11780\viewh11640\viewkind0
\deftab720
\pard\pardeftab720\li365\fi-366\sa60\partightenfactor0
\f0\fs22 \cf2 Thanks to Sheila and my family; thanks to my friends in Seattle and around the globe; thanks to the ever-patient and ever-awesome NetNewsWire beta testers. \
\pard\tx0\pardeftab720\li360\fi-361\sa60\partightenfactor0
\cf2 Thanks to {\field{\*\fldinst{HYPERLINK "https://shapeof.com/"}}{\fldrslt Gus Mueller}} for {\field{\*\fldinst{HYPERLINK "https://github.com/ccgus/fmdb"}}{\fldrslt FMDB}} by {\field{\*\fldinst{HYPERLINK "http://flyingmeat.com/"}}{\fldrslt Flying Meat Software}}. Thanks to {\field{\*\fldinst{HYPERLINK "https://github.com"}}{\fldrslt GitHub}} and {\field{\*\fldinst{HYPERLINK "https://slack.com"}}{\fldrslt Slack}} for making open source collaboration easy and fun. Thanks to {\field{\*\fldinst{HYPERLINK "https://benubois.com/"}}{\fldrslt Ben Ubois}} at {\field{\*\fldinst{HYPERLINK "https://feedbin.com/"}}{\fldrslt Feedbin}} for all the extra help with syncing and article rendering \'97\'a0and for {\field{\*\fldinst{HYPERLINK "https://feedbin.com/blog/2019/03/11/the-future-of-full-content/"}}{\fldrslt hosting the server for the Reader view}}.}

View File

@@ -289,15 +289,24 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
self.masterFeedViewController = rootSplitViewController.viewController(for: .primary) as? MasterFeedViewController
self.masterFeedViewController.coordinator = self
self.masterFeedViewController?.navigationController?.delegate = self
if let navController = self.masterFeedViewController?.navigationController {
navController.delegate = self
configureNavigationController(navController)
}
self.masterTimelineViewController = rootSplitViewController.viewController(for: .supplementary) as? MasterTimelineViewController
self.masterTimelineViewController?.coordinator = self
self.masterTimelineViewController?.navigationController?.delegate = self
if let navController = self.masterTimelineViewController?.navigationController {
navController.delegate = self
configureNavigationController(navController)
}
self.articleViewController = rootSplitViewController.viewController(for: .secondary) as? ArticleViewController
self.articleViewController?.coordinator = self
if let navController = self.articleViewController?.navigationController {
configureNavigationController(navController)
}
for sectionNode in treeController.rootNode.childNodes {
markExpanded(sectionNode)
shadowTable.append((sectionID: "", feedNodes: [FeedNode]()))
@@ -1314,6 +1323,10 @@ extension SceneCoordinator: UISplitViewControllerDelegate {
}
}
func splitViewController(_ svc: UISplitViewController, willChangeTo displayMode: UISplitViewController.DisplayMode) {
articleViewController?.updateUnreadCountIndicator(forDisplayMode: displayMode)
}
}
// MARK: UINavigationControllerDelegate
@@ -1370,6 +1383,31 @@ extension SceneCoordinator: UINavigationControllerDelegate {
private extension SceneCoordinator {
func configureNavigationController(_ navController: UINavigationController) {
let scrollEdge = UINavigationBarAppearance()
scrollEdge.configureWithOpaqueBackground()
scrollEdge.shadowColor = nil
scrollEdge.shadowImage = UIImage()
let standard = UINavigationBarAppearance()
standard.shadowColor = .opaqueSeparator
standard.shadowImage = UIImage()
navController.navigationBar.standardAppearance = standard
navController.navigationBar.compactAppearance = standard
navController.navigationBar.scrollEdgeAppearance = scrollEdge
navController.navigationBar.compactScrollEdgeAppearance = scrollEdge
navController.navigationBar.tintColor = AppAssets.primaryAccentColor
let toolbarAppearance = UIToolbarAppearance()
navController.toolbar.standardAppearance = toolbarAppearance
navController.toolbar.compactAppearance = toolbarAppearance
navController.toolbar.scrollEdgeAppearance = toolbarAppearance
navController.toolbar.tintColor = AppAssets.primaryAccentColor
}
func markArticlesWithUndo(_ articles: [Article], statusKey: ArticleStatus.Key, flag: Bool, completion: (() -> Void)? = nil) {
guard let undoManager = undoManager,
let markReadCommand = MarkStatusCommand(initialArticles: articles, statusKey: statusKey, flag: flag, undoManager: undoManager, completion: completion) else {

View File

@@ -28,6 +28,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging {
coordinator = SceneCoordinator(rootSplitViewController: rootViewController)
rootViewController.coordinator = coordinator
rootViewController.delegate = coordinator
rootViewController.showsSecondaryOnlyButton = true
coordinator.restoreWindowState(session.stateRestorationActivity)

View File

@@ -0,0 +1,32 @@
//
// AboutData.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 02/10/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import Foundation
struct AboutData: Codable {
var AppCredits: [AppCredit]
var AdditionalContributors: [Contributor]
var ThanksMarkdown: AttributedString {
let dataURL = Bundle.main.url(forResource: "Thanks", withExtension: "md")!
return try! AttributedString(markdown: Data(contentsOf: dataURL), options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace))
}
struct AppCredit: Codable {
var name: String
var role: String
var url: String?
}
struct Contributor: Codable {
var name: String
var url: String?
}
}

View File

@@ -0,0 +1,117 @@
//
// AboutView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 02/10/2022.
// Copyright © 2022 Ranchero Software. All rights reserved.
//
import SwiftUI
struct AboutView: View {
private var about: AboutData!
init() {
guard let path = Bundle.main.path(forResource: "About", ofType: "plist") else {
fatalError("The about plist really should exist.")
}
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
about = try! PropertyListDecoder().decode(AboutData.self, from: data)
}
var body: some View {
List {
Section(header: aboutHeaderView) {}
Section(header: Text("Credits")) {
ForEach(0..<about.AppCredits.count, id: \.self) { i in
creditView(about.AppCredits[i])
}
}
Section(header: Text("Additional Contributors")) {
ForEach(0..<about.AdditionalContributors.count, id: \.self) { i in
contributorView(about.AdditionalContributors[i])
}
}
Section(header: Text("Thanks"), footer: thanks, content: {})
Section(footer: copyright, content: {})
}
.listStyle(.insetGrouped)
.navigationTitle(Text("About"))
.navigationBarTitleDisplayMode(.inline)
}
var aboutHeaderView: some View {
HStack {
Spacer()
VStack(alignment: .center, spacing: 8) {
Image("About")
.resizable()
.frame(width: 75, height: 75)
Text(Bundle.main.appName)
.font(.headline)
Text("By Brent Simmons and the Ranchero Software team.")
.font(.subheadline)
Text("[netnewswire.com](https://netnewswire.com)")
}
Spacer()
}
.textCase(.none)
.multilineTextAlignment(.center)
}
func creditView(_ appCredit: AboutData.AppCredit) -> some View {
HStack {
Text(appCredit.role)
Spacer()
Text(appCredit.name)
.foregroundColor(.secondary)
}
.onTapGesture {
guard let url = appCredit.url else { return }
if let creditURL = URL(string: url) {
UIApplication.shared.open(creditURL)
}
}
}
func contributorView(_ contributor: AboutData.Contributor) -> some View {
HStack {
Text(contributor.name)
Spacer()
}
.onTapGesture {
guard let url = contributor.url else { return }
if let contributorURL = URL(string: url) {
UIApplication.shared.open(contributorURL)
}
}
}
var thanks: some View {
Text(about.ThanksMarkdown)
.multilineTextAlignment(.center)
.font(.callout)
}
var copyright: some View {
Text(verbatim: "Copyright © Brent Simmons 2002 - \(Calendar.current.component(.year, from: .now))")
}
}
struct AboutView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
AboutView()
}
}
}

View File

@@ -1,59 +0,0 @@
//
// AboutViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 4/25/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class AboutViewController: UITableViewController {
@IBOutlet weak var aboutTextView: UITextView!
@IBOutlet weak var creditsTextView: UITextView!
@IBOutlet weak var acknowledgmentsTextView: UITextView!
@IBOutlet weak var thanksTextView: UITextView!
@IBOutlet weak var dedicationTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
configureCell(file: "About", textView: aboutTextView)
configureCell(file: "Credits", textView: creditsTextView)
configureCell(file: "Thanks", textView: thanksTextView)
configureCell(file: "Dedication", textView: dedicationTextView)
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 32.0, y: 0.0, width: 0.0, height: 0.0))
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
buildLabel.textColor = UIColor.gray
buildLabel.text = NSLocalizedString("Copyright © 2002-2022 Brent Simmons", comment: "Copyright")
buildLabel.numberOfLines = 0
buildLabel.sizeToFit()
buildLabel.translatesAutoresizingMaskIntoConstraints = false
let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: buildLabel.frame.width, height: buildLabel.frame.height + 10.0))
wrapperView.translatesAutoresizingMaskIntoConstraints = false
wrapperView.addSubview(buildLabel)
tableView.tableFooterView = wrapperView
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
private extension AboutViewController {
func configureCell(file: String, textView: UITextView) {
let url = Bundle.main.url(forResource: file, withExtension: "rtf")!
let string = try! NSAttributedString(url: url, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
textView.attributedText = string
textView.textColor = UIColor.label
textView.adjustsFontForContentSizeCategory = true
textView.font = .preferredFont(forTextStyle: .body)
}
}

View File

@@ -72,9 +72,9 @@ class ArticleThemesTableViewController: UITableViewController, Logging {
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
guard let cell = tableView.cellForRow(at: indexPath),
let themeName = cell.textLabel?.text,
let theme = ArticleThemesManager.shared.articleThemeWithThemeName(themeName),
!theme.isAppTheme else { return nil }
let themeName = cell.textLabel?.text else { return nil }
guard !ArticleThemesManager.shared.articleThemeWithThemeName(themeName).isAppTheme else { return nil }
let deleteTitle = NSLocalizedString("Delete", comment: "Delete")
let deleteAction = UIContextualAction(style: .normal, title: deleteTitle) { [weak self] (action, view, completion) in

View File

@@ -283,8 +283,8 @@ class SettingsViewController: UITableViewController, Logging {
openURL("https://netnewswire.com/slack")
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
case 8:
let timeline = UIStoryboard.settings.instantiateController(ofType: AboutViewController.self)
self.navigationController?.pushViewController(timeline, animated: true)
let hosting = UIHostingController(rootView: AboutView())
self.navigationController?.pushViewController(hosting, animated: true)
default:
break
}