diff --git a/Multiplatform/Shared/Inspector/InspectorModel.swift b/Multiplatform/Shared/Inspector/InspectorModel.swift new file mode 100644 index 000000000..df87bc5d5 --- /dev/null +++ b/Multiplatform/Shared/Inspector/InspectorModel.swift @@ -0,0 +1,103 @@ +// +// InspectorModel.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 18/7/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation +import UserNotifications +import RSCore +import Account +#if os(macOS) +import AppKit +#else +import UIKit +#endif + + +class InspectorModel: ObservableObject { + + @Published var notificationSettings: UNNotificationSettings? + @Published var editedName: String = "" + @Published var shouldUpdate: Bool = false + @Published var notifyAboutNewArticles: Bool = false { + didSet { + updateNotificationSettings() + } + } + @Published var alwaysShowReaderView: Bool = false + private let centre = UNUserNotificationCenter.current() + private var selectedWebFeed: WebFeed? + private var selectedFolder: Folder? + private var selectedAccount: Account? + + init() { + getNotificationSettings() + } + + func getNotificationSettings() { + centre.getNotificationSettings { (settings) in + DispatchQueue.main.async { + self.notificationSettings = settings + if settings.authorizationStatus == .authorized { + #if os(macOS) + NSApplication.shared.registerForRemoteNotifications() + #else + UIApplication.shared.registerForRemoteNotifications() + #endif + } + } + } + } + + func configure(with feed: WebFeed) { + selectedWebFeed = feed + notifyAboutNewArticles = selectedWebFeed?.isNotifyAboutNewArticles ?? false + alwaysShowReaderView = selectedWebFeed?.isArticleExtractorAlwaysOn ?? false + editedName = feed.nameForDisplay + } + + func configure(with folder: Folder) { + selectedFolder = folder + editedName = folder.nameForDisplay + } + + func configure(with account: Account) { + selectedAccount = account + editedName = account.nameForDisplay + } + + func updateNotificationSettings() { + guard let feed = selectedWebFeed, + let settings = notificationSettings + else { return } + if settings.authorizationStatus == .denied { + notifyAboutNewArticles = false + } else if settings.authorizationStatus == .authorized { + feed.isNotifyAboutNewArticles = notifyAboutNewArticles + } else { + UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { [weak self] (granted, error) in + self?.updateNotificationSettings() + if granted { + DispatchQueue.main.async { + self?.selectedWebFeed!.isNotifyAboutNewArticles = self?.notifyAboutNewArticles + #if os(macOS) + NSApplication.shared.registerForRemoteNotifications() + #else + UIApplication.shared.registerForRemoteNotifications() + #endif + } + } else { + DispatchQueue.main.async { + self?.notifyAboutNewArticles = false + } + } + } + } + } + + +} + diff --git a/Multiplatform/Shared/Inspector/InspectorView.swift b/Multiplatform/Shared/Inspector/InspectorView.swift index e9f1775d8..f93ddd9d6 100644 --- a/Multiplatform/Shared/Inspector/InspectorView.swift +++ b/Multiplatform/Shared/Inspector/InspectorView.swift @@ -14,8 +14,7 @@ struct InspectorView: View { @Environment(\.presentationMode) var presentationMode @StateObject private var feedIconImageLoader = FeedIconImageLoader() - @State private var editedName: String = "" - @State private var shouldUpdate: Bool = false + @StateObject private var inspectorModel = InspectorModel() var sidebarItem: SidebarItem @ViewBuilder @@ -23,13 +22,13 @@ struct InspectorView: View { switch sidebarItem.representedType { case .webFeed: WebFeedInspectorView - .modifier(InspectorPlatformModifier(shouldUpdate: $shouldUpdate)) + .modifier(InspectorPlatformModifier(shouldUpdate: $inspectorModel.shouldUpdate)) case .folder: FolderInspectorView - .modifier(InspectorPlatformModifier(shouldUpdate: $shouldUpdate)) + .modifier(InspectorPlatformModifier(shouldUpdate: $inspectorModel.shouldUpdate)) case .account: AccountInspectorView - .modifier(InspectorPlatformModifier(shouldUpdate: $shouldUpdate)) + .modifier(InspectorPlatformModifier(shouldUpdate: $inspectorModel.shouldUpdate)) default: EmptyView() } @@ -44,7 +43,7 @@ struct InspectorView: View { IconImageView(iconImage: image) .frame(width: 30, height: 30) } - TextField("", text: $editedName) + TextField("", text: $inspectorModel.editedName) } } @@ -52,6 +51,15 @@ struct InspectorView: View { Divider() #endif + Section(content: { + Toggle("Notify About New Articles", isOn: $inspectorModel.notifyAboutNewArticles) + Toggle("Always Show Reader View", isOn: $inspectorModel.alwaysShowReaderView) + }) + + #if os(macOS) + Divider() + #endif + Section(header: Text("Home Page URL").bold()) { Text((sidebarItem.feed as? WebFeed)?.homePageURL ?? "") } @@ -71,18 +79,18 @@ struct InspectorView: View { presentationMode.wrappedValue.dismiss() }) Button("Done", action: { - shouldUpdate = true - }) + inspectorModel.shouldUpdate = true + }).keyboardShortcut(.defaultAction) }.padding(.top) #endif } .onAppear { - editedName = sidebarItem.nameForDisplay + inspectorModel.configure(with: sidebarItem.feed as! WebFeed) feedIconImageLoader.loadImage(for: sidebarItem.feed!) - }.onChange(of: shouldUpdate) { value in + }.onChange(of: inspectorModel.shouldUpdate) { value in if value == true { - if editedName.trimmingWhitespace.count > 0 { - (sidebarItem.feed as? WebFeed)?.editedName = editedName + if inspectorModel.editedName.trimmingWhitespace.count > 0 { + (sidebarItem.feed as? WebFeed)?.editedName = inspectorModel.editedName } else { (sidebarItem.feed as? WebFeed)?.editedName = nil } @@ -102,7 +110,7 @@ struct InspectorView: View { IconImageView(iconImage: image) .frame(width: 30, height: 30) } - TextField("", text: $editedName) + TextField("", text: $inspectorModel.editedName) } } @@ -113,20 +121,20 @@ struct InspectorView: View { presentationMode.wrappedValue.dismiss() }) Button("Done", action: { - shouldUpdate = true - }) + inspectorModel.shouldUpdate = true + }).keyboardShortcut(.defaultAction) }.padding(.top) #endif } .onAppear { - editedName = sidebarItem.nameForDisplay + inspectorModel.configure(with: sidebarItem.represented as! Folder) feedIconImageLoader.loadImage(for: sidebarItem.feed!) } - .onChange(of: shouldUpdate) { value in + .onChange(of: inspectorModel.shouldUpdate) { value in if value == true { - if editedName.trimmingWhitespace.count > 0 { - (sidebarItem.feed as? Folder)?.name = editedName + if inspectorModel.editedName.trimmingWhitespace.count > 0 { + (sidebarItem.feed as? Folder)?.name = inspectorModel.editedName } else { (sidebarItem.feed as? Folder)?.name = nil } @@ -146,7 +154,7 @@ struct InspectorView: View { .aspectRatio(contentMode: .fit) .frame(width: 30, height: 30) } - TextField("", text: $editedName) + TextField("", text: $inspectorModel.editedName) } } @@ -157,17 +165,18 @@ struct InspectorView: View { presentationMode.wrappedValue.dismiss() }) Button("Done", action: { - shouldUpdate = true - }) + inspectorModel.shouldUpdate = true + }).keyboardShortcut(.defaultAction) }.padding(.top) #endif } .onAppear { - editedName = sidebarItem.nameForDisplay - }.onChange(of: shouldUpdate) { value in + inspectorModel.configure(with: sidebarItem.represented as! Account) + } + .onChange(of: inspectorModel.shouldUpdate) { value in if value == true { - if editedName.trimmingWhitespace.count > 0 { - (sidebarItem.represented as? Account)?.name = editedName + if inspectorModel.editedName.trimmingWhitespace.count > 0 { + (sidebarItem.represented as? Account)?.name = inspectorModel.editedName } else { (sidebarItem.represented as? Account)?.name = nil } diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 6abcdd2cd..f8ad4b4e8 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ 17930ED524AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; }; 1799E6A924C2F93F00511E91 /* InspectorPlatformModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */; }; 1799E6AA24C2F93F00511E91 /* InspectorPlatformModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */; }; + 1799E6CD24C320D600511E91 /* InspectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6CC24C320D600511E91 /* InspectorModel.swift */; }; + 1799E6CE24C320D600511E91 /* InspectorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6CC24C320D600511E91 /* InspectorModel.swift */; }; 179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; }; 179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; }; 17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; }; @@ -1830,6 +1832,7 @@ 17897AC924C281A40014BA03 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = ""; }; 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedView.swift; sourceTree = ""; }; 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPlatformModifier.swift; sourceTree = ""; }; + 1799E6CC24C320D600511E91 /* InspectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorModel.swift; sourceTree = ""; }; 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = ""; }; 17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = ""; }; 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedModel.swift; sourceTree = ""; }; @@ -2647,6 +2650,7 @@ children = ( 17897AC924C281A40014BA03 /* InspectorView.swift */, 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */, + 1799E6CC24C320D600511E91 /* InspectorModel.swift */, ); path = Inspector; sourceTree = ""; @@ -5246,6 +5250,7 @@ 51E4990224A808BB00B667CB /* ColorHash.swift in Sources */, 51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */, 5177476224B3BC4700EB0F74 /* SettingsAboutView.swift in Sources */, + 1799E6CD24C320D600511E91 /* InspectorModel.swift in Sources */, 51E4991924A8090A00B667CB /* CacheCleaner.swift in Sources */, 51E498F724A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */, 175942AA24AD533200585066 /* RefreshInterval.swift in Sources */, @@ -5392,6 +5397,7 @@ 51392D1C24AC19A000BE0D35 /* SidebarExpandedContainers.swift in Sources */, 51C0515F24A77DF800194D5E /* MainApp.swift in Sources */, 51B54A4324B5499B0014348B /* WebViewProvider.swift in Sources */, + 1799E6CE24C320D600511E91 /* InspectorModel.swift in Sources */, 514E6C0024AD255D00AC6F6E /* PreviewArticles.swift in Sources */, 1729529524AA1CAA00D65E66 /* GeneralPreferencesView.swift in Sources */, 1769E32724BC5B6C000E1E8E /* AddAccountModel.swift in Sources */,