Skip to content

Commit

Permalink
Merge pull request #2 from Ranchero-Software/ios-release
Browse files Browse the repository at this point in the history
merge
  • Loading branch information
stuartbreckenridge committed May 15, 2020
2 parents 1f7d40a + 49e51d4 commit b56d20d
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 47 deletions.
8 changes: 8 additions & 0 deletions NetNewsWire.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -2405,6 +2409,7 @@
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */,
516AE9DE2372269A007DEEAA /* IconImage.swift */,
B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */,
DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
18 changes: 18 additions & 0 deletions Shared/Extensions/URL-Extensions.swift
Original file line number Diff line number Diff line change
@@ -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
}

}
4 changes: 4 additions & 0 deletions iOS/Article/ArticleViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?) {
Expand Down
32 changes: 32 additions & 0 deletions iOS/Article/WebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import RSCore
import Account
import Articles
import SafariServices
import MessageUI

protocol WebViewControllerDelegate: class {
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
}
9 changes: 9 additions & 0 deletions iOS/KeyboardManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]))

Expand Down Expand Up @@ -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
}

Expand Down
28 changes: 20 additions & 8 deletions iOS/MasterFeed/MasterFeedViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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? {
Expand All @@ -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)
}
}
Expand Down
39 changes: 30 additions & 9 deletions iOS/MasterTimeline/MarkAsReadAlertController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,28 @@
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()
return
}

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)
Expand All @@ -32,18 +39,20 @@ 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.",
comment: "You can turn this confirmation off in settings.")
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?()
}
Expand All @@ -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
}
Expand Down
Loading

0 comments on commit b56d20d

Please sign in to comment.