diff --git a/Multiplatform/Shared/Add/AddWebFeedView.swift b/Multiplatform/Shared/Add/AddWebFeedView.swift index d7ec81f2c..692484e4f 100644 --- a/Multiplatform/Shared/Add/AddWebFeedView.swift +++ b/Multiplatform/Shared/Add/AddWebFeedView.swift @@ -10,49 +10,6 @@ import SwiftUI import Account import RSCore -fileprivate enum AddWebFeedError: LocalizedError { - - case none, alreadySubscribed, initialDownload, noFeeds - - var errorDescription: String? { - switch self { - case .alreadySubscribed: - return NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder") - case .initialDownload: - return NSLocalizedString("Can’t add this feed because of a download error.", comment: "Feed finder") - case .noFeeds: - return NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder") - default: - return nil - } - } - -} - -fileprivate class AddWebFeedViewModel: ObservableObject { - - @Published var providedURL: String = "" - @Published var providedName: String = "" - @Published var selectedFolderIndex: Int = 0 - @Published var addFeedError: AddWebFeedError? { - didSet { - addFeedError != .none ? (showError = true) : (showError = false) - } - } - @Published var showError: Bool = false - @Published var containers: [Container] = [] - @Published var showProgressIndicator: Bool = false - - init() { - for account in AccountManager.shared.sortedActiveAccounts { - containers.append(account) - if let sortedFolders = account.sortedFolders { - containers.append(contentsOf: sortedFolders) - } - } - } - -} struct AddWebFeedView: View { @@ -62,15 +19,28 @@ struct AddWebFeedView: View { @ViewBuilder var body: some View { #if os(iOS) iosForm + .onAppear { + viewModel.pasteUrlFromPasteboard() + } + .onReceive(viewModel.$shouldDismiss, perform: { dismiss in + if dismiss == true { + presentationMode.wrappedValue.dismiss() + } + }) + #else macForm .onAppear { - pasteUrlFromPasteboard() + viewModel.pasteUrlFromPasteboard() }.alert(isPresented: $viewModel.showError) { Alert(title: Text("Oops"), message: Text(viewModel.addFeedError!.localizedDescription), dismissButton: Alert.Button.cancel({ - viewModel.addFeedError = .none + viewModel.addFeedError = AddWebFeedError.none })) - } + }.onReceive(viewModel.$shouldDismiss, perform: { dismiss in + if dismiss == true { + presentationMode.wrappedValue.dismiss() + } + }) #endif } @@ -117,7 +87,7 @@ struct AddWebFeedView: View { .help("Cancel Add Feed") , trailing: Button("Add", action: { - addWebFeed() + viewModel.addWebFeed() }) .disabled(!viewModel.providedURL.isValidURL) .help("Add Feed") @@ -168,94 +138,17 @@ struct AddWebFeedView: View { .help("Cancel Add Feed") Button("Add", action: { - addWebFeed() + viewModel.addWebFeed() }) .disabled(!viewModel.providedURL.isValidURL) .help("Add Feed") } } - #if os(macOS) - func pasteUrlFromPasteboard() { - guard let stringFromPasteboard = urlStringFromPasteboard, stringFromPasteboard.isValidURL else { - return - } - viewModel.providedURL = stringFromPasteboard - } - #endif + } -private extension AddWebFeedView { - - #if os(macOS) - var urlStringFromPasteboard: String? { - if let urlString = NSPasteboard.urlString(from: NSPasteboard.general) { - return urlString.normalizedURL - } - return nil - } - #else - var urlStringFromPasteboard: String? { - if let urlString = UIPasteboard.general.url?.absoluteString { - return urlString.normalizedURL - } - return nil - } - #endif - - struct AccountAndFolderSpecifier { - let account: Account - let folder: Folder? - } - - func accountAndFolderFromContainer(_ container: Container) -> AccountAndFolderSpecifier? { - if let account = container as? Account { - return AccountAndFolderSpecifier(account: account, folder: nil) - } - if let folder = container as? Folder, let account = folder.account { - return AccountAndFolderSpecifier(account: account, folder: folder) - } - return nil - } - - func addWebFeed() { - if let account = accountAndFolderFromContainer(viewModel.containers[viewModel.selectedFolderIndex])?.account { - - viewModel.showProgressIndicator = true - - let container = viewModel.containers[viewModel.selectedFolderIndex] - - if account.hasWebFeed(withURL: viewModel.providedURL) { - viewModel.addFeedError = .alreadySubscribed - viewModel.showProgressIndicator = false - return - } - - account.createWebFeed(url: viewModel.providedURL, name: viewModel.providedName, container: container, completion: { result in - viewModel.showProgressIndicator = false - switch result { - case .success(let feed): - NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed]) - presentationMode.wrappedValue.dismiss() - case .failure(let error): - switch error { - case AccountError.createErrorAlreadySubscribed: - self.viewModel.addFeedError = .alreadySubscribed - return - case AccountError.createErrorNotFound: - self.viewModel.addFeedError = .noFeeds - return - default: - print("Error") - } - } - }) - } - } -} - - struct AddFeedView_Previews: PreviewProvider { static var previews: some View { AddWebFeedView() diff --git a/Multiplatform/Shared/Add/AddWebFeedViewModel.swift b/Multiplatform/Shared/Add/AddWebFeedViewModel.swift new file mode 100644 index 000000000..a8f7a5280 --- /dev/null +++ b/Multiplatform/Shared/Add/AddWebFeedViewModel.swift @@ -0,0 +1,132 @@ +// +// AddWebFeedViewModel.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 4/7/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation +import Account +import RSCore +import SwiftUI + +enum AddWebFeedError: LocalizedError { + + case none, alreadySubscribed, initialDownload, noFeeds + + var errorDescription: String? { + switch self { + case .alreadySubscribed: + return NSLocalizedString("Can’t add this feed because you’ve already subscribed to it.", comment: "Feed finder") + case .initialDownload: + return NSLocalizedString("Can’t add this feed because of a download error.", comment: "Feed finder") + case .noFeeds: + return NSLocalizedString("Can’t add a feed because no feed was found.", comment: "Feed finder") + default: + return nil + } + } + +} + +class AddWebFeedViewModel: ObservableObject { + + @Published var shouldDismiss: Bool = false + @Published var providedURL: String = "" + @Published var providedName: String = "" + @Published var selectedFolderIndex: Int = 0 + @Published var addFeedError: AddWebFeedError? { + didSet { + addFeedError != AddWebFeedError.none ? (showError = true) : (showError = false) + } + } + @Published var showError: Bool = false + @Published var containers: [Container] = [] + @Published var showProgressIndicator: Bool = false + + init() { + for account in AccountManager.shared.sortedActiveAccounts { + containers.append(account) + if let sortedFolders = account.sortedFolders { + containers.append(contentsOf: sortedFolders) + } + } + } + + func pasteUrlFromPasteboard() { + guard let stringFromPasteboard = urlStringFromPasteboard, stringFromPasteboard.isValidURL else { + return + } + providedURL = stringFromPasteboard + } + + #if os(macOS) + var urlStringFromPasteboard: String? { + if let urlString = NSPasteboard.urlString(from: NSPasteboard.general) { + return urlString.normalizedURL + } + return nil + } + #else + var urlStringFromPasteboard: String? { + if let urlString = UIPasteboard.general.url?.absoluteString { + return urlString.normalizedURL + } + return nil + } + #endif + + struct AccountAndFolderSpecifier { + let account: Account + let folder: Folder? + } + + func accountAndFolderFromContainer(_ container: Container) -> AccountAndFolderSpecifier? { + if let account = container as? Account { + return AccountAndFolderSpecifier(account: account, folder: nil) + } + if let folder = container as? Folder, let account = folder.account { + return AccountAndFolderSpecifier(account: account, folder: folder) + } + return nil + } + + func addWebFeed() { + if let account = accountAndFolderFromContainer(containers[selectedFolderIndex])?.account { + + showProgressIndicator = true + + let container = containers[selectedFolderIndex] + + if account.hasWebFeed(withURL: providedURL) { + addFeedError = .alreadySubscribed + showProgressIndicator = false + return + } + + account.createWebFeed(url: providedURL, name: providedName, container: container, completion: { [weak self] result in + self?.showProgressIndicator = false + switch result { + case .success(let feed): + NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed]) + self?.shouldDismiss = true + case .failure(let error): + switch error { + case AccountError.createErrorAlreadySubscribed: + self?.addFeedError = .alreadySubscribed + return + case AccountError.createErrorNotFound: + self?.addFeedError = .noFeeds + return + default: + print("Error") + } + } + }) + } + } + + + +} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 4b52e636b..56f3cdc60 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 17930ED524AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; }; 179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; }; 179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; }; + 17D232A824AFF10A0005F075 /* AddWebFeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedViewModel.swift */; }; + 17D232A924AFF10A0005F075 /* AddWebFeedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedViewModel.swift */; }; 3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; }; 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; }; 3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; @@ -1728,6 +1730,7 @@ 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedView.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 /* AddWebFeedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedViewModel.swift; sourceTree = ""; }; 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = ""; }; 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = ""; }; 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = ""; }; @@ -2408,6 +2411,7 @@ 17930ED224AF10CD00A9BA52 /* Add */ = { isa = PBXGroup; children = ( + 17D232A724AFF10A0005F075 /* AddWebFeedViewModel.swift */, 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */, ); path = Add; @@ -4841,6 +4845,7 @@ 51A576BE24AE637400078888 /* ArticleView.swift in Sources */, 51E4995324A8734D00B667CB /* RedditFeedProvider-Extensions.swift in Sources */, 172199C924AB228900A31D04 /* SettingsView.swift in Sources */, + 17D232A824AFF10A0005F075 /* AddWebFeedViewModel.swift in Sources */, 51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */, 51E498F624A8085D00B667CB /* SearchFeedDelegate.swift in Sources */, 51E498F224A8085D00B667CB /* SmartFeedsController.swift in Sources */, @@ -4915,6 +4920,7 @@ 51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */, 51E4990424A808C300B667CB /* WebFeedIconDownloader.swift in Sources */, 51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */, + 17D232A924AFF10A0005F075 /* AddWebFeedViewModel.swift in Sources */, 51E4993324A867E700B667CB /* AppNotifications.swift in Sources */, 51E4990624A808C300B667CB /* ImageDownloader.swift in Sources */, 51E4994F24A8734C00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,