Add initial support for per feed notifications
This commit is contained in:
parent
aba0d15cb6
commit
cc187875d9
|
@ -123,6 +123,15 @@ public final class Feed: DisplayNameProvider, Renamable, UnreadCountProvider, Ha
|
|||
}
|
||||
}
|
||||
|
||||
public var isNotifyAboutNewArticles: Bool? {
|
||||
get {
|
||||
return metadata.isNotifyAboutNewArticles
|
||||
}
|
||||
set {
|
||||
metadata.isNotifyAboutNewArticles = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public var isArticleExtractorAlwaysOn: Bool? {
|
||||
get {
|
||||
return metadata.isArticleExtractorAlwaysOn
|
||||
|
|
|
@ -24,6 +24,7 @@ final class FeedMetadata: Codable {
|
|||
case editedName
|
||||
case authors
|
||||
case contentHash
|
||||
case isNotifyAboutNewArticles
|
||||
case isArticleExtractorAlwaysOn
|
||||
case conditionalGetInfo
|
||||
case subscriptionID
|
||||
|
@ -78,10 +79,18 @@ final class FeedMetadata: Codable {
|
|||
}
|
||||
}
|
||||
|
||||
var isNotifyAboutNewArticles: Bool? {
|
||||
didSet {
|
||||
if isNotifyAboutNewArticles != oldValue {
|
||||
valueDidChange(.isNotifyAboutNewArticles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isArticleExtractorAlwaysOn: Bool? {
|
||||
didSet {
|
||||
if isArticleExtractorAlwaysOn != oldValue {
|
||||
valueDidChange(.contentHash)
|
||||
valueDidChange(.isArticleExtractorAlwaysOn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import AppKit
|
||||
import UserNotifications
|
||||
import Articles
|
||||
import RSTree
|
||||
import RSWeb
|
||||
|
@ -16,8 +17,9 @@ import RSCore
|
|||
var appDelegate: AppDelegate!
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UnreadCountProvider {
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider {
|
||||
|
||||
var userNotificationManager: UserNotificationManager!
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
var imageDownloader: ImageDownloader!
|
||||
var authorAvatarDownloader: AuthorAvatarDownloader!
|
||||
|
@ -130,7 +132,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
}
|
||||
let localAccount = AccountManager.shared.defaultAccount
|
||||
DefaultFeedsImporter.importIfNeeded(isFirstRun, account: localAccount)
|
||||
|
||||
|
||||
let tempDirectory = NSTemporaryDirectory()
|
||||
let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String)
|
||||
let cacheFolder = (tempDirectory as NSString).appendingPathComponent(bundleIdentifier)
|
||||
|
@ -179,6 +181,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
refreshTimer = AccountRefreshTimer()
|
||||
syncTimer = ArticleStatusSyncTimer()
|
||||
|
||||
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { (granted, error) in
|
||||
if granted {
|
||||
DispatchQueue.main.async {
|
||||
NSApplication.shared.registerForRemoteNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
userNotificationManager = UserNotificationManager()
|
||||
|
||||
#if RELEASE
|
||||
debugMenuItem.menu?.removeItem(debugMenuItem)
|
||||
DispatchQueue.main.async {
|
||||
|
@ -322,6 +335,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
return true
|
||||
}
|
||||
|
||||
// MARK: UNUserNotificationCenterDelegate
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
completionHandler([.alert, .badge, .sound])
|
||||
}
|
||||
|
||||
// MARK: Add Feed
|
||||
|
||||
func addFeed(_ urlString: String?, name: String? = nil, account: Account? = nil, folder: Folder? = nil) {
|
||||
|
|
|
@ -12,10 +12,11 @@ import Account
|
|||
|
||||
final class FeedInspectorViewController: NSViewController, Inspector {
|
||||
|
||||
@IBOutlet var imageView: NSImageView?
|
||||
@IBOutlet var nameTextField: NSTextField?
|
||||
@IBOutlet var homePageURLTextField: NSTextField?
|
||||
@IBOutlet var urlTextField: NSTextField?
|
||||
@IBOutlet weak var imageView: NSImageView?
|
||||
@IBOutlet weak var nameTextField: NSTextField?
|
||||
@IBOutlet weak var homePageURLTextField: NSTextField?
|
||||
@IBOutlet weak var urlTextField: NSTextField?
|
||||
@IBOutlet weak var isNotifyAboutNewArticlesCheckBox: NSButton!
|
||||
@IBOutlet weak var isReaderViewAlwaysOnCheckBox: NSButton?
|
||||
|
||||
private var feed: Feed? {
|
||||
|
@ -51,6 +52,10 @@ final class FeedInspectorViewController: NSViewController, Inspector {
|
|||
}
|
||||
|
||||
// MARK: Actions
|
||||
@IBAction func isNotifyAboutNewArticlesChanged(_ sender: Any) {
|
||||
feed?.isNotifyAboutNewArticles = (isNotifyAboutNewArticlesCheckBox?.state ?? .off) == .on ? true : false
|
||||
}
|
||||
|
||||
@IBAction func isReaderViewAlwaysOnChanged(_ sender: Any) {
|
||||
feed?.isArticleExtractorAlwaysOn = (isReaderViewAlwaysOnCheckBox?.state ?? .off) == .on ? true : false
|
||||
}
|
||||
|
@ -89,6 +94,7 @@ private extension FeedInspectorViewController {
|
|||
updateName()
|
||||
updateHomePageURL()
|
||||
updateFeedURL()
|
||||
updateNotifyAboutNewArticles()
|
||||
updateIsReaderViewAlwaysOn()
|
||||
|
||||
view.needsLayout = true
|
||||
|
@ -135,6 +141,10 @@ private extension FeedInspectorViewController {
|
|||
urlTextField?.stringValue = feed?.url ?? ""
|
||||
}
|
||||
|
||||
func updateNotifyAboutNewArticles() {
|
||||
isNotifyAboutNewArticlesCheckBox?.state = (feed?.isNotifyAboutNewArticles ?? false) ? .on : .off
|
||||
}
|
||||
|
||||
func updateIsReaderViewAlwaysOn() {
|
||||
isReaderViewAlwaysOnCheckBox?.state = (feed?.isArticleExtractorAlwaysOn ?? false) ? .on : .off
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="cfG-Pn-VJS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="cfG-Pn-VJS">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15400"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15504"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
|
@ -34,11 +34,11 @@
|
|||
<objects>
|
||||
<viewController title="Feed" storyboardIdentifier="Feed" showSeguePresentationStyle="single" id="sfH-oR-GXm" customClass="FeedInspectorViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="ecA-UY-KEd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="256" height="298"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="256" height="332"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="H9X-OG-K0p">
|
||||
<rect key="frame" x="104" y="230" width="48" height="48"/>
|
||||
<rect key="frame" x="104" y="264" width="48" height="48"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="48" id="1Cy-0w-dBg"/>
|
||||
<constraint firstAttribute="height" constant="48" id="edb-lw-Ict"/>
|
||||
|
@ -46,7 +46,7 @@
|
|||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSNetwork" id="MZ2-89-Bje"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="IWu-80-XC5">
|
||||
<rect key="frame" x="20" y="166" width="216" height="56"/>
|
||||
<rect key="frame" x="20" y="200" width="216" height="56"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="56" id="zV3-AX-gyC"/>
|
||||
</constraints>
|
||||
|
@ -104,13 +104,24 @@ Field</string>
|
|||
<action selector="isReaderViewAlwaysOnChanged:" target="sfH-oR-GXm" id="rsD-0e-ksP"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZBX-E8-k9c">
|
||||
<rect key="frame" x="18" y="164" width="179" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Notify About New Articles" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Bw5-c7-yDX">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="isNotifyAboutNewArticlesChanged:" target="sfH-oR-GXm" id="Vx9-pQ-RnP"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="nH2-ab-KJ5" firstAttribute="top" secondItem="IWu-80-XC5" secondAttribute="bottom" constant="20" id="2OX-H3-BJ0"/>
|
||||
<constraint firstItem="zm0-15-BFy" firstAttribute="top" secondItem="2WO-Iu-p5e" secondAttribute="bottom" constant="4" id="2fb-QO-XIm"/>
|
||||
<constraint firstItem="IWu-80-XC5" firstAttribute="top" secondItem="H9X-OG-K0p" secondAttribute="bottom" constant="8" symbolic="YES" id="4WB-WJ-3Z4"/>
|
||||
<constraint firstItem="ZBX-E8-k9c" firstAttribute="top" secondItem="IWu-80-XC5" secondAttribute="bottom" constant="20" id="5L7-aZ-vdg"/>
|
||||
<constraint firstItem="nH2-ab-KJ5" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="8pK-lW-xQk"/>
|
||||
<constraint firstItem="H9X-OG-K0p" firstAttribute="centerX" secondItem="ecA-UY-KEd" secondAttribute="centerX" id="9CA-KA-HEg"/>
|
||||
<constraint firstItem="nH2-ab-KJ5" firstAttribute="top" secondItem="ZBX-E8-k9c" secondAttribute="bottom" constant="20" id="CpA-X9-EbP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Vvk-KG-JlG" secondAttribute="bottom" constant="20" id="IxJ-5N-NhL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ju6-Zo-8X4" secondAttribute="trailing" constant="20" symbolic="YES" id="Jzi-tP-TIw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Vvk-KG-JlG" secondAttribute="trailing" constant="20" symbolic="YES" id="KAS-A7-TxB"/>
|
||||
|
@ -120,6 +131,7 @@ Field</string>
|
|||
<constraint firstAttribute="trailing" secondItem="IWu-80-XC5" secondAttribute="trailing" constant="20" symbolic="YES" id="WW6-xR-Zue"/>
|
||||
<constraint firstItem="H9X-OG-K0p" firstAttribute="top" secondItem="ecA-UY-KEd" secondAttribute="top" constant="20" symbolic="YES" id="Z6q-PN-wOC"/>
|
||||
<constraint firstItem="zm0-15-BFy" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="aho-BJ-kmB"/>
|
||||
<constraint firstItem="ZBX-E8-k9c" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="cjR-0i-YNG"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2WO-Iu-p5e" secondAttribute="trailing" constant="20" symbolic="YES" id="dLU-a6-nfx"/>
|
||||
<constraint firstAttribute="trailing" secondItem="zm0-15-BFy" secondAttribute="trailing" constant="20" symbolic="YES" id="js6-b2-FIR"/>
|
||||
<constraint firstItem="IWu-80-XC5" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="r6h-Z0-g7b"/>
|
||||
|
@ -131,6 +143,7 @@ Field</string>
|
|||
<connections>
|
||||
<outlet property="homePageURLTextField" destination="zm0-15-BFy" id="0Jh-yy-mnF"/>
|
||||
<outlet property="imageView" destination="H9X-OG-K0p" id="Rm6-X6-csH"/>
|
||||
<outlet property="isNotifyAboutNewArticlesCheckBox" destination="ZBX-E8-k9c" id="FWc-Ds-LUy"/>
|
||||
<outlet property="isReaderViewAlwaysOnCheckBox" destination="nH2-ab-KJ5" id="xPg-P5-3cr"/>
|
||||
<outlet property="nameTextField" destination="IWu-80-XC5" id="zg4-5h-hoP"/>
|
||||
<outlet property="urlTextField" destination="Vvk-KG-JlG" id="bcl-fq-3nQ"/>
|
||||
|
@ -138,7 +151,7 @@ Field</string>
|
|||
</viewController>
|
||||
<customObject id="1ho-ZO-Gkb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="67" y="46"/>
|
||||
<point key="canvasLocation" x="67" y="69.5"/>
|
||||
</scene>
|
||||
<!--Folder-->
|
||||
<scene sceneID="8By-fa-WDQ">
|
||||
|
@ -189,7 +202,7 @@ Field</string>
|
|||
</viewController>
|
||||
<customObject id="4SD-ni-Scy" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="67" y="329"/>
|
||||
<point key="canvasLocation" x="67" y="389"/>
|
||||
</scene>
|
||||
<!--Builtin Smart Feed-->
|
||||
<scene sceneID="dFq-3d-JKW">
|
||||
|
@ -231,7 +244,7 @@ Field</string>
|
|||
</viewController>
|
||||
<customObject id="3Xn-vX-2s9" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="67" y="553"/>
|
||||
<point key="canvasLocation" x="67" y="613"/>
|
||||
</scene>
|
||||
<!--Nothing to inspect-->
|
||||
<scene sceneID="lUc-e1-dN7">
|
||||
|
|
|
@ -218,6 +218,8 @@
|
|||
51FA73B72332D5F70090D516 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FA73B62332D5F70090D516 /* ArticleExtractorButton.swift */; };
|
||||
51FD40C72341555A00880194 /* UIImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FD40BD2341555600880194 /* UIImage-Extensions.swift */; };
|
||||
51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */; };
|
||||
51FE10032345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
||||
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
||||
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */; };
|
||||
55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */; };
|
||||
5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */; };
|
||||
|
@ -899,6 +901,7 @@
|
|||
51FA73B62332D5F70090D516 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51FD40BD2341555600880194 /* UIImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineUnreadCountView.swift; sourceTree = "<group>"; };
|
||||
51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = "<group>"; };
|
||||
557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsReaderAPIAccountView.swift; sourceTree = "<group>"; };
|
||||
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = "<group>"; };
|
||||
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1456,6 +1459,14 @@
|
|||
path = "Article Extractor";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51FE0FF9234552490056195D /* UserNotifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51FE10022345529D0056195D /* UserNotificationManager.swift */,
|
||||
);
|
||||
path = UserNotifications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6581C73620CED60100F4AD34 /* SafariExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1879,6 +1890,7 @@
|
|||
849A97861ED9ECEF007D329B /* Article Styles */,
|
||||
84DAEE201F86CAE00058304B /* Importers */,
|
||||
8444C9011FED81880051386C /* Exporters */,
|
||||
51FE0FF9234552490056195D /* UserNotifications */,
|
||||
84F2D5341FC22FCB00998D64 /* SmartFeeds */,
|
||||
848F6AE31FC29CFA002D422E /* Favicons */,
|
||||
845213211FCA5B10003B6E93 /* Images */,
|
||||
|
@ -2830,6 +2842,7 @@
|
|||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
|
||||
5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */,
|
||||
512E09352268B25900BDCFDD /* UISplitViewController-Extensions.swift in Sources */,
|
||||
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */,
|
||||
51C452A022650A1900C03939 /* FeedIconDownloader.swift in Sources */,
|
||||
51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */,
|
||||
51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */,
|
||||
|
@ -2953,6 +2966,7 @@
|
|||
5144EA43227A380F00D19003 /* ExportOPMLWindowController.swift in Sources */,
|
||||
842611A21FCB769D0086A189 /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */,
|
||||
51FE10032345529D0056195D /* UserNotificationManager.swift in Sources */,
|
||||
D5907DB22004BB37005947E5 /* ScriptingObjectContainer.swift in Sources */,
|
||||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */,
|
||||
8405DD8A2213E0E3008CE1BF /* DetailContainerView.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// NotificationManager.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 10/2/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Account
|
||||
import Articles
|
||||
import UserNotifications
|
||||
|
||||
final class UserNotificationManager: NSObject {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil)
|
||||
}
|
||||
|
||||
@objc func accountDidDownloadArticles(_ note: Notification) {
|
||||
guard let articles = note.userInfo?[Account.UserInfoKey.newArticles] as? Set<Article> else {
|
||||
return
|
||||
}
|
||||
|
||||
for article in articles {
|
||||
if let feed = article.feed, feed.isNotifyAboutNewArticles ?? false {
|
||||
sendNotification(feed: feed, article: article)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension UserNotificationManager {
|
||||
|
||||
private func sendNotification(feed: Feed, article: Article) {
|
||||
let content = UNMutableNotificationContent()
|
||||
|
||||
content.title = feed.nameForDisplay
|
||||
content.body = article.title ?? article.summary ?? ""
|
||||
content.sound = UNNotificationSound.default
|
||||
|
||||
let request = UNNotificationRequest.init(identifier: "articleID:\(article.articleID)", content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
}
|
||||
|
||||
}
|
|
@ -10,14 +10,13 @@ import UIKit
|
|||
import RSCore
|
||||
import RSWeb
|
||||
import Account
|
||||
import UserNotifications
|
||||
import BackgroundTasks
|
||||
import os.log
|
||||
|
||||
var appDelegate: AppDelegate!
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate, UnreadCountProvider {
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, UnreadCountProvider {
|
||||
|
||||
private var syncBackgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
|
||||
|
||||
|
@ -34,6 +33,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||
|
||||
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "application")
|
||||
|
||||
var userNotificationManager: UserNotificationManager!
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
var imageDownloader: ImageDownloader!
|
||||
var authorAvatarDownloader: AuthorAvatarDownloader!
|
||||
|
@ -90,7 +90,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
userNotificationManager = UserNotificationManager()
|
||||
|
||||
syncTimer = ArticleStatusSyncTimer()
|
||||
|
||||
#if DEBUG
|
||||
|
@ -171,6 +174,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||
logMessage(message, type: .debug)
|
||||
}
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
completionHandler([.alert, .badge, .sound])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: App Initialization
|
||||
|
|
|
@ -52,6 +52,9 @@ struct FeedInspectorView : View {
|
|||
Spacer()
|
||||
}) {
|
||||
TextField("Feed Name", text: $viewModel.name)
|
||||
Toggle(isOn: $viewModel.isNotifyAboutNewArticles) {
|
||||
Text("Notify About New Articles")
|
||||
}
|
||||
Toggle(isOn: $viewModel.isArticleExtractorAlwaysOn) {
|
||||
Text("Always Show Reader View")
|
||||
}
|
||||
|
@ -108,6 +111,16 @@ struct FeedInspectorView : View {
|
|||
}
|
||||
}
|
||||
|
||||
var isNotifyAboutNewArticles: Bool {
|
||||
get {
|
||||
return feed.isNotifyAboutNewArticles ?? false
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
feed.isNotifyAboutNewArticles = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isArticleExtractorAlwaysOn: Bool {
|
||||
get {
|
||||
return feed.isArticleExtractorAlwaysOn ?? false
|
||||
|
|
Loading…
Reference in New Issue