NetNewsWire/Multiplatform/Shared/Add/AddFeedView.swift

223 lines
5.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// AddFeedView.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 3/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
import RSCore
fileprivate enum AddFeedError: LocalizedError {
case none, alreadySubscribed, initialDownload, noFeeds
var errorDescription: String? {
switch self {
case .alreadySubscribed:
return NSLocalizedString("Cant add this feed because youve already subscribed to it.", comment: "Feed finder")
case .initialDownload:
return NSLocalizedString("Cant add this feed because of a download error.", comment: "Feed finder")
case .noFeeds:
return NSLocalizedString("Cant add a feed because no feed was found.", comment: "Feed finder")
default:
return nil
}
}
}
fileprivate class AddFeedViewModel: ObservableObject {
@Published var providedURL: String = ""
@Published var providedName: String = ""
@Published var selectedFolderIndex: Int = 0
@Published var addFeedError: AddFeedError? {
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 AddFeedView: View {
@Environment(\.presentationMode) private var presentationMode
@ObservedObject private var viewModel = AddFeedViewModel()
@ViewBuilder var body: some View {
#if os(iOS)
iosForm
#else
macForm
.onAppear {
pasteUrlFromPasteboard()
}.alert(isPresented: $viewModel.showError) {
Alert(title: Text("Oops"), message: Text(viewModel.addFeedError!.localizedDescription), dismissButton: Alert.Button.cancel({
viewModel.addFeedError = .none
}))
}
#endif
}
#if os(macOS)
var macForm: some View {
VStack(alignment: .leading) {
urlTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
providedNameTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
folderPicker
buttonStack
}
.padding()
.frame(minWidth: 400)
}
#endif
#if os(iOS)
var iosForm: some View {
NavigationLink {
List {
Text("PLACEHOLDER")
}.listStyle(InsetGroupedListStyle())
}
}
#endif
var urlTextField: some View {
HStack {
Text("Feed URL:")
TextField("URL", text: $viewModel.providedURL)
}
}
var providedNameTextField: some View {
HStack(alignment: .lastTextBaseline) {
Text("Name:")
TextField("Optional", text: $viewModel.providedName)
}
}
var folderPicker: some View {
Picker("Folder", selection: $viewModel.selectedFolderIndex, content: {
ForEach(0..<viewModel.containers.count, id: \.self, content: { index in
if let containerName = (viewModel.containers[index] as? DisplayNameProvider)?.nameForDisplay {
if viewModel.containers[index] is Folder {
Text("\(viewModel.containers[index].account?.nameForDisplay ?? "") / \(containerName)").tag(index)
} else {
Text(containerName).tag(index)
}
}
})
})
}
var buttonStack: some View {
HStack {
if viewModel.showProgressIndicator == true {
ProgressView()
.frame(width: 25, height: 25)
}
Spacer()
Button("Cancel", action: {
presentationMode.wrappedValue.dismiss()
})
Button("Add", action: {
addWebFeed()
}).disabled(!viewModel.providedURL.isValidURL)
}
}
#if os(macOS)
func pasteUrlFromPasteboard() {
guard let stringFromPasteboard = urlStringFromPasteboard, stringFromPasteboard.isValidURL else {
return
}
viewModel.providedURL = stringFromPasteboard
}
#endif
}
private extension AddFeedView {
var urlStringFromPasteboard: String? {
if let urlString = NSPasteboard.urlString(from: NSPasteboard.general) {
return urlString.normalizedURL
}
return nil
}
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 {
AddFeedView()
}
}