Merge pull request #2 from Ranchero-Software/ios-release

merge
This commit is contained in:
Stuart Breckenridge 2020-05-15 10:04:56 +08:00 committed by GitHub
commit b56d20d507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 177 additions and 47 deletions

View File

@ -674,6 +674,9 @@
D5F4EDB720074D6500B9E363 /* WebFeed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* WebFeed+Scriptability.swift */; };
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
DD82AB0A231003F6002269DF /* SharingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD82AB09231003F6002269DF /* SharingTests.swift */; };
DF41F3AE245EFCD7004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; };
DF41F3C8245EFD45004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; };
DF41F3C9245EFD46004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; };
FF3ABF13232599810074C542 /* ArticleSorterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF09232599450074C542 /* ArticleSorterTests.swift */; };
FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
@ -1635,6 +1638,7 @@
D5F4EDB620074D6500B9E363 /* WebFeed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebFeed+Scriptability.swift"; sourceTree = "<group>"; };
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = "<group>"; };
DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = "<group>"; };
FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = "<group>"; };
FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorter.swift; sourceTree = "<group>"; };
FFD43E372340F320009E5CA3 /* MarkAsReadAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkAsReadAlertController.swift; sourceTree = "<group>"; };
@ -2405,6 +2409,7 @@
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */,
516AE9DE2372269A007DEEAA /* IconImage.swift */,
B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */,
DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -3852,6 +3857,7 @@
65ED401C235DEF6C0081F399 /* FaviconGenerator.swift in Sources */,
65ED401D235DEF6C0081F399 /* RefreshInterval.swift in Sources */,
65ED401E235DEF6C0081F399 /* TimelineCellData.swift in Sources */,
DF41F3C9245EFD46004EFB01 /* URL-Extensions.swift in Sources */,
65ED401F235DEF6C0081F399 /* BuiltinSmartFeedInspectorViewController.swift in Sources */,
65ED4020235DEF6C0081F399 /* AppDelegate+Scriptability.swift in Sources */,
65ED4021235DEF6C0081F399 /* NNW3Document.swift in Sources */,
@ -4038,6 +4044,7 @@
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */,
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */,
DF41F3C8245EFD45004EFB01 /* URL-Extensions.swift in Sources */,
FFD43E412340F488009E5CA3 /* MarkAsReadAlertController.swift in Sources */,
51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */,
51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */,
@ -4156,6 +4163,7 @@
51EF0F922279CA620050506E /* AccountsAddTableCellView.swift in Sources */,
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
8405DDA522168C62008CE1BF /* TimelineContainerViewController.swift in Sources */,
DF41F3AE245EFCD7004EFB01 /* URL-Extensions.swift in Sources */,
844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */,
848D578E21543519005FFAD5 /* PasteboardWebFeed.swift in Sources */,
5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */,

View File

@ -0,0 +1,18 @@
//
// URL-Extensions.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 03/05/2020.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
extension URL {
/// Extracts email address from a `URL` with a `mailto` scheme, otherwise `nil`.
var emailAddress: String? {
scheme == "mailto" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil
}
}

View File

@ -250,6 +250,10 @@ class ArticleViewController: UIViewController {
@IBAction func showActivityDialog(_ sender: Any) {
currentWebViewController?.showActivityDialog(popOverBarButtonItem: actionBarButtonItem)
}
@objc func toggleReaderView(_ sender: Any?) {
currentWebViewController?.toggleArticleExtractor()
}
// MARK: Keyboard Shortcuts
@objc func navigateToTimeline(_ sender: Any?) {

View File

@ -12,6 +12,7 @@ import RSCore
import Account
import Articles
import SafariServices
import MessageUI
protocol WebViewControllerDelegate: class {
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
@ -316,6 +317,30 @@ extension WebViewController: WKNavigationDelegate {
let vc = SFSafariViewController(url: url)
self.present(vc, animated: true)
}
} else if components?.scheme == "mailto" {
decisionHandler(.cancel)
guard let emailAddress = url.emailAddress else {
return
}
if MFMailComposeViewController.canSendMail() {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.setToRecipients([emailAddress])
mailComposeViewController.mailComposeDelegate = self
self.present(mailComposeViewController, animated: true, completion: {})
} else {
let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), message: NSLocalizedString("This device cannot send emails.", comment: "This device cannot send emails."), preferredStyle: .alert)
alert.addAction(.init(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
} else if components?.scheme == "tel" {
decisionHandler(.cancel)
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil)
}
} else {
decisionHandler(.allow)
}
@ -672,3 +697,10 @@ private extension WebViewController {
}
}
// MARK: MFMailComposeViewControllerDelegate
extension WebViewController: MFMailComposeViewControllerDelegate {
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
self.dismiss(animated: true, completion: nil)
}
}

View File

@ -138,6 +138,9 @@ private extension KeyboardManager {
let goToStarredTitle = NSLocalizedString("Go To Starred", comment: "Go To Starred")
keys.append(KeyboardManager.createKeyCommand(title: goToStarredTitle, action: "goToStarred:", input: "3", modifiers: [.command]))
let gotoSettings = NSLocalizedString("Go To Settings", comment: "Go To Settings")
keys.append(KeyboardManager.createKeyCommand(title: gotoSettings, action: "goToSettings:", input: ",", modifiers: [.command]))
let articleSearchTitle = NSLocalizedString("Article Search", comment: "Article Search")
keys.append(KeyboardManager.createKeyCommand(title: articleSearchTitle, action: "articleSearch:", input: "f", modifiers: [.command, .alternate]))
@ -186,6 +189,12 @@ private extension KeyboardManager {
let toggleStarredTitle = NSLocalizedString("Toggle Starred Status", comment: "Toggle Starred Status")
keys.append(KeyboardManager.createKeyCommand(title: toggleStarredTitle, action: "toggleStarred:", input: "l", modifiers: [.command, .shift]))
let toggleSidebar = NSLocalizedString("Toggle Sidebar", comment: "Toggle Sidebar")
keys.append(KeyboardManager.createKeyCommand(title: toggleSidebar, action: "toggleSidebar:", input: "s", modifiers: [.command, .control]))
let toggleReaderView = NSLocalizedString("Toggle Reader View", comment: "Toggle Reader View")
keys.append(KeyboardManager.createKeyCommand(title: toggleReaderView, action: "toggleReaderView:", input: "r", modifiers: [.command, .shift]))
return keys
}

View File

@ -458,7 +458,18 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
self.reloadAllVisibleCells()
}
}
@objc func markAllAsRead(_ sender: Any) {
guard let indexPath = tableView.indexPathForSelectedRow, let contentView = tableView.cellForRow(at: indexPath)?.contentView else {
return
}
let title = NSLocalizedString("Mark All as Read", comment: "Mark All as Read")
MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markAllAsReadInTimeline()
}
}
// MARK: API
func restoreSelectionIfNecessary(adjustScroll: Bool) {
@ -937,7 +948,7 @@ private extension MasterFeedViewController {
guard let node = dataSource.itemIdentifier(for: indexPath),
coordinator.unreadCountFor(node) > 0,
let feed = node.representedObject as? WebFeed,
let articles = try? feed.fetchArticles() else {
let articles = try? feed.fetchArticles(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
@ -947,8 +958,9 @@ private extension MasterFeedViewController {
completion(true)
}
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, cancelCompletion: cancel) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
self?.coordinator.markAllAsRead(Array(articles))
completion(true)
}
@ -1026,7 +1038,7 @@ private extension MasterFeedViewController {
}
let articles = Array(fetchedArticles)
return markAllAsReadAction(articles: articles, nameForDisplay: articleFetcher.nameForDisplay)
return markAllAsReadAction(articles: articles, nameForDisplay: articleFetcher.nameForDisplay, indexPath: indexPath)
}
func markAllAsReadAction(account: Account) -> UIAction? {
@ -1038,16 +1050,16 @@ private extension MasterFeedViewController {
return markAllAsReadAction(articles: articles, nameForDisplay: account.nameForDisplay)
}
func markAllAsReadAction(articles: [Article], nameForDisplay: String) -> UIAction? {
guard articles.canMarkAllAsRead() else {
func markAllAsReadAction(articles: [Article], nameForDisplay: String, indexPath: IndexPath? = nil) -> UIAction? {
guard articles.canMarkAllAsRead(), let indexPath = indexPath, let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
let localizedMenuText = NSLocalizedString("Mark All as Read in “%@”", comment: "Command")
let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, nameForDisplay) as String
let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markAllAsRead(articles)
}
}

View File

@ -9,13 +9,20 @@
import Foundation
import UIKit
protocol MarkAsReadAlertControllerSourceType {}
extension CGRect: MarkAsReadAlertControllerSourceType {}
extension UIView: MarkAsReadAlertControllerSourceType {}
extension UIBarButtonItem: MarkAsReadAlertControllerSourceType {}
struct MarkAsReadAlertController {
static func confirm(_ controller: UIViewController?,
coordinator: SceneCoordinator?,
confirmTitle: String,
cancelCompletion: (() -> Void)? = nil,
completion: @escaping () -> Void) {
static func confirm<T>(_ controller: UIViewController?,
coordinator: SceneCoordinator?,
confirmTitle: String,
sourceType: T,
cancelCompletion: (() -> Void)? = nil,
completion: @escaping () -> Void) where T: MarkAsReadAlertControllerSourceType {
guard let controller = controller, let coordinator = coordinator else {
completion()
@ -23,7 +30,7 @@ struct MarkAsReadAlertController {
}
if AppDefaults.confirmMarkAllAsRead {
let alertController = MarkAsReadAlertController.alert(coordinator: coordinator, confirmTitle: confirmTitle, cancelCompletion: cancelCompletion) { _ in
let alertController = MarkAsReadAlertController.alert(coordinator: coordinator, confirmTitle: confirmTitle, cancelCompletion: cancelCompletion, sourceType: sourceType) { _ in
completion()
}
controller.present(alertController, animated: true)
@ -32,10 +39,12 @@ struct MarkAsReadAlertController {
}
}
private static func alert(coordinator: SceneCoordinator,
private static func alert<T>(coordinator: SceneCoordinator,
confirmTitle: String,
cancelCompletion: (() -> Void)?,
completion: @escaping (UIAlertAction) -> Void) -> UIAlertController {
sourceType: T,
completion: @escaping (UIAlertAction) -> Void) -> UIAlertController where T: MarkAsReadAlertControllerSourceType {
let title = NSLocalizedString("Mark As Read", comment: "Mark As Read")
let message = NSLocalizedString("You can turn this confirmation off in settings.",
@ -43,7 +52,7 @@ struct MarkAsReadAlertController {
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
let settingsTitle = NSLocalizedString("Open Settings", comment: "Open Settings")
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let alertController = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { _ in
cancelCompletion?()
}
@ -55,6 +64,18 @@ struct MarkAsReadAlertController {
alertController.addAction(markAction)
alertController.addAction(settingsAction)
alertController.addAction(cancelAction)
if let barButtonItem = sourceType as? UIBarButtonItem {
alertController.popoverPresentationController?.barButtonItem = barButtonItem
}
if let rect = sourceType as? CGRect {
alertController.popoverPresentationController?.sourceRect = rect
}
if let view = sourceType as? UIView {
alertController.popoverPresentationController?.sourceView = view
}
return alertController
}

View File

@ -116,8 +116,21 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
@IBAction func markAllAsRead(_ sender: Any) {
let title = NSLocalizedString("Mark All as Read", comment: "Mark All as Read")
MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title) { [weak self] in
self?.coordinator.markAllAsReadInTimeline()
if let source = sender as? UIBarButtonItem {
MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title, sourceType: source) { [weak self] in
self?.coordinator.markAllAsReadInTimeline()
}
}
if let _ = sender as? UIKeyCommand {
guard let indexPath = tableView.indexPathForSelectedRow, let contentView = tableView.cellForRow(at: indexPath)?.contentView else {
return
}
MarkAsReadAlertController.confirm(self, coordinator: coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markAllAsReadInTimeline()
}
}
}
@ -156,7 +169,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
@objc func showFeedInspector(_ sender: UITapGestureRecognizer) {
coordinator.showFeedInspector()
}
// MARK: API
func restoreSelectionIfNecessary(adjustScroll: Bool) {
@ -260,11 +273,11 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
popoverController.sourceRect = CGRect(x: view.frame.size.width/2, y: view.frame.size.height/2, width: 1, height: 1)
}
if let action = self.markAboveAsReadAlertAction(article, completion: completion) {
if let action = self.markAboveAsReadAlertAction(article, indexPath: indexPath, completion: completion) {
alert.addAction(action)
}
if let action = self.markBelowAsReadAlertAction(article, completion: completion) {
if let action = self.markBelowAsReadAlertAction(article, indexPath: indexPath, completion: completion) {
alert.addAction(action)
}
@ -272,7 +285,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
alert.addAction(action)
}
if let action = self.markAllInFeedAsReadAlertAction(article, completion: completion) {
if let action = self.markAllInFeedAsReadAlertAction(article, indexPath: indexPath, completion: completion) {
alert.addAction(action)
}
@ -317,11 +330,11 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
actions.append(self.toggleArticleStarStatusAction(article))
if let action = self.markAboveAsReadAction(article) {
if let action = self.markAboveAsReadAction(article, indexPath: indexPath) {
actions.append(action)
}
if let action = self.markBelowAsReadAction(article) {
if let action = self.markBelowAsReadAction(article, indexPath: indexPath) {
actions.append(action)
}
@ -329,7 +342,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
actions.append(action)
}
if let action = self.markAllInFeedAsReadAction(article) {
if let action = self.markAllInFeedAsReadAction(article, indexPath: indexPath) {
actions.append(action)
}
@ -696,38 +709,38 @@ private extension MasterTimelineViewController {
return action
}
func markAboveAsReadAction(_ article: Article) -> UIAction? {
guard coordinator.canMarkAboveAsRead(for: article) else {
func markAboveAsReadAction(_ article: Article, indexPath: IndexPath) -> UIAction? {
guard coordinator.canMarkAboveAsRead(for: article), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
let title = NSLocalizedString("Mark Above as Read", comment: "Mark Above as Read")
let image = AppAssets.markAboveAsReadImage
let action = UIAction(title: title, image: image) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markAboveAsRead(article)
}
}
return action
}
func markBelowAsReadAction(_ article: Article) -> UIAction? {
guard coordinator.canMarkBelowAsRead(for: article) else {
func markBelowAsReadAction(_ article: Article, indexPath: IndexPath) -> UIAction? {
guard coordinator.canMarkBelowAsRead(for: article), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
let title = NSLocalizedString("Mark Below as Read", comment: "Mark Below as Read")
let image = AppAssets.markBelowAsReadImage
let action = UIAction(title: title, image: image) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markBelowAsRead(article)
}
}
return action
}
func markAboveAsReadAlertAction(_ article: Article, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
guard coordinator.canMarkAboveAsRead(for: article) else {
func markAboveAsReadAlertAction(_ article: Article, indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
guard coordinator.canMarkAboveAsRead(for: article), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
@ -737,7 +750,7 @@ private extension MasterTimelineViewController {
}
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, cancelCompletion: cancel) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
self?.coordinator.markAboveAsRead(article)
completion(true)
}
@ -745,8 +758,8 @@ private extension MasterTimelineViewController {
return action
}
func markBelowAsReadAlertAction(_ article: Article, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
guard coordinator.canMarkBelowAsRead(for: article) else {
func markBelowAsReadAlertAction(_ article: Article, indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
guard coordinator.canMarkBelowAsRead(for: article), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
@ -756,7 +769,7 @@ private extension MasterTimelineViewController {
}
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, cancelCompletion: cancel) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
self?.coordinator.markBelowAsRead(article)
completion(true)
}
@ -787,36 +800,37 @@ private extension MasterTimelineViewController {
return action
}
func markAllInFeedAsReadAction(_ article: Article) -> UIAction? {
func markAllInFeedAsReadAction(_ article: Article, indexPath: IndexPath) -> UIAction? {
guard let webFeed = article.webFeed else { return nil }
guard let fetchedArticles = try? webFeed.fetchArticles() else {
return nil
}
let articles = Array(fetchedArticles)
guard articles.canMarkAllAsRead() else {
guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
let localizedMenuText = NSLocalizedString("Mark All as Read in “%@”", comment: "Command")
let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, webFeed.nameForDisplay) as String
let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
self?.coordinator.markAllAsRead(articles)
}
}
return action
}
func markAllInFeedAsReadAlertAction(_ article: Article, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
func markAllInFeedAsReadAlertAction(_ article: Article, indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
guard let webFeed = article.webFeed else { return nil }
guard let fetchedArticles = try? webFeed.fetchArticles() else {
return nil
}
let articles = Array(fetchedArticles)
guard articles.canMarkAllAsRead() else {
guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
return nil
}
@ -827,7 +841,7 @@ private extension MasterTimelineViewController {
}
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, cancelCompletion: cancel) { [weak self] in
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
self?.coordinator.markAllAsRead(articles)
completion(true)
}

View File

@ -121,7 +121,11 @@ class RootSplitViewController: UISplitViewController {
@objc func goToStarred(_ sender: Any?) {
coordinator.selectStarredFeed()
}
@objc func goToSettings(_ sender: Any?) {
coordinator.showSettings()
}
@objc func toggleRead(_ sender: Any?) {
coordinator.toggleReadForCurrentArticle()
}
@ -129,5 +133,8 @@ class RootSplitViewController: UISplitViewController {
@objc func toggleStarred(_ sender: Any?) {
coordinator.toggleStarredForCurrentArticle()
}
@objc func toggleSidebar(_ sender: Any?) {
coordinator.toggleSidebar()
}
}

View File

@ -1199,7 +1199,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
func navigateToDetail() {
articleViewController?.focus()
}
func toggleSidebar() {
rootSplitViewController.preferredDisplayMode = rootSplitViewController.displayMode == .allVisible ? .primaryHidden : .allVisible
}
}
// MARK: UISplitViewControllerDelegate

View File

@ -32,6 +32,8 @@
<dict>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsText</key>
<string>1</string>
</dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>SafariExt</string>