NetNewsWire/Multiplatform/Shared/Add/AddWebFeedView.swift

264 lines
6.6 KiB
Swift
Raw Normal View History

2020-07-03 14:31:48 +02:00
//
// AddWebFeedView.swift
2020-07-03 14:31:48 +02:00
// NetNewsWire
//
// Created by Stuart Breckenridge on 3/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
import RSCore
fileprivate enum AddWebFeedError: LocalizedError {
2020-07-03 14:31:48 +02:00
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 AddWebFeedViewModel: ObservableObject {
2020-07-03 14:31:48 +02:00
@Published var providedURL: String = ""
@Published var providedName: String = ""
@Published var selectedFolderIndex: Int = 0
@Published var addFeedError: AddWebFeedError? {
2020-07-03 14:31:48 +02:00
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 {
2020-07-03 14:31:48 +02:00
@Environment(\.presentationMode) private var presentationMode
@ObservedObject private var viewModel = AddWebFeedViewModel()
2020-07-03 14:31:48 +02:00
@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 {
Form {
HStack {
Spacer()
Image(systemName: "globe").foregroundColor(.accentColor).font(.title)
Text("Add a Web Feed")
.font(.title)
Spacer()
}
2020-07-03 14:31:48 +02:00
urlTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
.help("The URL of the feed you want to add.")
2020-07-03 14:31:48 +02:00
providedNameTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
.help("The name of the feed. (Optional.)")
2020-07-03 14:31:48 +02:00
folderPicker
.help("Pick the folder you want to add the feed to.")
2020-07-03 14:31:48 +02:00
buttonStack
}
.padding()
.frame(minWidth: 450)
2020-07-03 14:31:48 +02:00
}
#endif
#if os(iOS)
@ViewBuilder var iosForm: some View {
NavigationView {
Form {
urlTextField
providedNameTextField
folderPicker
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Add Web Feed")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:
Button("Cancel", action: {
presentationMode.wrappedValue.dismiss()
})
.help("Cancel Add Feed")
, trailing:
Button("Add", action: {
addWebFeed()
})
.disabled(!viewModel.providedURL.isValidURL)
.help("Add Feed")
)
2020-07-03 14:31:48 +02:00
}
}
#endif
var urlTextField: some View {
HStack {
Text("Feed:")
2020-07-03 14:31:48 +02:00
TextField("URL", text: $viewModel.providedURL)
}
}
var providedNameTextField: some View {
HStack(alignment: .lastTextBaseline) {
Text("Name:")
2020-07-03 14:31:48 +02:00
TextField("Optional", text: $viewModel.providedName)
}
}
var folderPicker: some View {
Picker("Folder:", selection: $viewModel.selectedFolderIndex, content: {
2020-07-03 14:31:48 +02:00
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)
}
}
})
})
2020-07-03 14:31:48 +02:00
}
var buttonStack: some View {
HStack {
if viewModel.showProgressIndicator == true {
ProgressView()
.frame(width: 25, height: 25)
.help("Adding Feed")
2020-07-03 14:31:48 +02:00
}
Spacer()
Button("Cancel", action: {
presentationMode.wrappedValue.dismiss()
})
.help("Cancel Add Feed")
2020-07-03 14:31:48 +02:00
Button("Add", action: {
addWebFeed()
})
.disabled(!viewModel.providedURL.isValidURL)
.help("Add Feed")
2020-07-03 14:31:48 +02:00
}
}
#if os(macOS)
func pasteUrlFromPasteboard() {
guard let stringFromPasteboard = urlStringFromPasteboard, stringFromPasteboard.isValidURL else {
return
}
viewModel.providedURL = stringFromPasteboard
}
#endif
}
private extension AddWebFeedView {
2020-07-03 14:31:48 +02:00
#if os(macOS)
2020-07-03 14:31:48 +02:00
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
2020-07-03 14:31:48 +02:00
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()
2020-07-03 14:31:48 +02:00
}
}