diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index e5c134ea8..e28fc0475 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -48,11 +48,11 @@ final class ArticlesTable: DatabaseTable { // MARK: - Fetching Articles for Feed func fetchArticles(_ webFeedID: String) throws -> Set
{ - return try fetchArticles{ self.fetchArticlesForFeedID(webFeedID, withLimits: true, $0) } + return try fetchArticles{ self.fetchArticlesForFeedID(webFeedID, $0) } } func fetchArticlesAsync(_ webFeedID: String, _ completion: @escaping ArticleSetResultBlock) { - fetchArticlesAsync({ self.fetchArticlesForFeedID(webFeedID, withLimits: true, $0) }, completion) + fetchArticlesAsync({ self.fetchArticlesForFeedID(webFeedID, $0) }, completion) } func fetchArticles(_ webFeedIDs: Set) throws -> Set
{ @@ -204,7 +204,7 @@ final class ArticlesTable: DatabaseTable { return } - let fetchedArticles = self.fetchArticlesForFeedID(webFeedID, withLimits: false, database) //4 + let fetchedArticles = self.fetchArticlesForFeedID(webFeedID, database) //4 let fetchedArticlesDictionary = fetchedArticles.dictionary() let newArticles = self.findAndSaveNewArticles(incomingArticles, fetchedArticlesDictionary, database) //5 @@ -626,7 +626,7 @@ final class ArticlesTable: DatabaseTable { return } - let sql = "update statuses set read = true where dateArrived Set
{ + func fetchArticlesForFeedID(_ webFeedID: String, _ database: FMDatabase) -> Set
{ return fetchArticlesWithWhereClause(database, whereClause: "articles.feedID = ?", parameters: [webFeedID as AnyObject]) } diff --git a/Shared/Article Rendering/main.js b/Shared/Article Rendering/main.js index 6c79007fd..184df9008 100644 --- a/Shared/Article Rendering/main.js +++ b/Shared/Article Rendering/main.js @@ -3,7 +3,6 @@ function wrapFrames() { document.querySelectorAll("iframe").forEach(element => { if (element.height > 0 || parseInt(element.style.height) > 0) return; - var wrapper = document.createElement("div"); wrapper.classList.add("iframeWrap"); element.parentNode.insertBefore(wrapper, element); diff --git a/Shared/Extensions/URL-Extensions.swift b/Shared/Extensions/URL-Extensions.swift index a9577c9e1..bbe70a1b1 100644 --- a/Shared/Extensions/URL-Extensions.swift +++ b/Shared/Extensions/URL-Extensions.swift @@ -28,4 +28,13 @@ extension URL { #endif } + func valueFor(_ parameter: String) -> String? { + guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false), + let queryItems = components.queryItems, + let value = queryItems.first(where: { $0.name == parameter })?.value else { + return nil + } + return value + + } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 9f689c837..c6bd80fbf 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -353,6 +353,7 @@ extension WebViewController: WKNavigationDelegate { if MFMailComposeViewController.canSendMail() { let mailComposeViewController = MFMailComposeViewController() mailComposeViewController.setToRecipients([emailAddress]) + mailComposeViewController.setSubject(url.valueFor("subject") ?? "") mailComposeViewController.mailComposeDelegate = self self.present(mailComposeViewController, animated: true, completion: {}) } else { diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift index 3fcf7506f..dc5b21ed6 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeader.swift @@ -21,12 +21,22 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView { get { if unreadCount > 0 { let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity") - return "\(name) \(unreadCount) \(unreadLabel)" + return "\(name) \(unreadCount) \(unreadLabel) \(expandedStateMessage) " } else { - return name + return "\(name) \(expandedStateMessage) " } } } + + private var expandedStateMessage: String { + set {} + get { + if disclosureExpanded { + return NSLocalizedString("Expanded", comment: "Disclosure button expanded state for accessibility") + } + return NSLocalizedString("Collapsed", comment: "Disclosure button collapsed state for accessibility") + } + } var unreadCount: Int { get { diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index e5f923085..0d266ffad 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -599,7 +599,7 @@ extension MasterFeedViewController: UIContextMenuInteractionDelegate { var actions = [accountInfoAction, deactivateAction] - if let markAllAction = self.markAllAsReadAction(account: account) { + if let markAllAction = self.markAllAsReadAction(account: account, contentView: interaction.view) { actions.insert(markAllAction, at: 1) } @@ -1135,23 +1135,31 @@ private extension MasterFeedViewController { return markAllAsReadAction(articles: articles, nameForDisplay: feed.nameForDisplay, indexPath: indexPath) } - func markAllAsReadAction(account: Account) -> UIAction? { + func markAllAsReadAction(account: Account, contentView: UIView?) -> UIAction? { guard let fetchedArticles = try? account.fetchArticles(FetchType.unread) else { return nil } let articles = Array(fetchedArticles) - return markAllAsReadAction(articles: articles, nameForDisplay: account.nameForDisplay) + return markAllAsReadAction(articles: articles, nameForDisplay: account.nameForDisplay, contentView: contentView) } 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 { + guard let indexPath = indexPath, + let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else { + return nil + } + + return markAllAsReadAction(articles: articles, nameForDisplay: nameForDisplay, contentView: contentView) + } + + func markAllAsReadAction(articles: [Article], nameForDisplay: String, contentView: UIView?) -> UIAction? { + guard articles.canMarkAllAsRead(), let contentView = 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, sourceType: contentView) { [weak self] in self?.coordinator.markAllAsRead(articles) diff --git a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift index 25047b0c0..0ed459aa5 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineTableViewCell.swift @@ -256,8 +256,9 @@ private extension MasterTimelineTableViewCell { } func updateAccessiblityLabel() { - var label = cellData.read ? "" : "\(NSLocalizedString("Unread", comment: "Unread")), " - label += "\(cellData.feedName), \(cellData.title), \(cellData.summary), \(cellData.dateString)" + let starredStatus = cellData.starred ? "\(NSLocalizedString("Starred", comment: "Starred article for accessibility")), " : "" + let unreadStatus = cellData.read ? "" : "\(NSLocalizedString("Unread", comment: "Unread")), " + let label = starredStatus + unreadStatus + "\(cellData.feedName), \(cellData.title), \(cellData.summary), \(cellData.dateString)" accessibilityLabel = label } diff --git a/iOS/MasterTimeline/MasterTimelineTitleView.swift b/iOS/MasterTimeline/MasterTimelineTitleView.swift index 76e4fbbaa..9c81ac7ee 100644 --- a/iOS/MasterTimeline/MasterTimelineTitleView.swift +++ b/iOS/MasterTimeline/MasterTimelineTitleView.swift @@ -18,7 +18,20 @@ class MasterTimelineTitleView: UIView { private lazy var pointerInteraction: UIPointerInteraction = { UIPointerInteraction(delegate: self) }() - + + override var accessibilityLabel: String? { + set { } + get { + if let name = label.text { + let unreadLabel = NSLocalizedString("unread", comment: "Unread label for accessiblity") + return "\(name) \(unreadCountView.unreadCount) \(unreadLabel)" + } + else { + return nil + } + } + } + func buttonize() { heightAnchor.constraint(equalToConstant: 40.0).isActive = true accessibilityTraits = .button @@ -28,6 +41,7 @@ class MasterTimelineTitleView: UIView { } func debuttonize() { + heightAnchor.constraint(equalToConstant: 40.0).isActive = true accessibilityTraits.remove(.button) if #available(iOS 13.4, *) { removeInteraction(pointerInteraction) diff --git a/iOS/Resources/Credits.rtf b/iOS/Resources/Credits.rtf index a4c868701..1ccb2905a 100644 --- a/iOS/Resources/Credits.rtf +++ b/iOS/Resources/Credits.rtf @@ -1,4 +1,4 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2511 +{\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;} @@ -17,4 +17,4 @@ Under-the-hood magic and CSS stylin\'92s: {\field{\*\fldinst{HYPERLINK "https:// \pard\pardeftab720\li355\fi-356\sa60\partightenfactor0 \cf2 Help book: {\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\ \pard\pardeftab720\li358\fi-359\sa60\partightenfactor0 -\cf2 And more {\field{\*\fldinst{HYPERLINK "https://github.com/brentsimmons/NetNewsWire/graphs/contributors"}}{\fldrslt contributors}}!} \ No newline at end of file +\cf2 And featuring contributions from {\field{\*\fldinst{HYPERLINK "https://blog.rizwan.dev/"}}{\fldrslt Rizwan Mohamed Ibrahim}}, {\field{\*\fldinst{HYPERLINK "https://stuartbreckenridge.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}}!} \ No newline at end of file diff --git a/iOS/ShareExtension/Info.plist b/iOS/ShareExtension/Info.plist index 4c31edb0d..c10c1fe15 100644 --- a/iOS/ShareExtension/Info.plist +++ b/iOS/ShareExtension/Info.plist @@ -35,7 +35,7 @@ NSExtensionActivationSupportsWebURLWithMaxCount 1 NSExtensionActivationSupportsText - 1 + NSExtensionJavaScriptPreprocessingFile SafariExt diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index eb3906f45..24e4615db 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it -MARKETING_VERSION = 5.0.1 -CURRENT_PROJECT_VERSION = 46 +MARKETING_VERSION = 5.0.3 +CURRENT_PROJECT_VERSION = 50 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon