From ad678f2fc17dbd52c658630bb53b43238e3db552 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 5 Dec 2020 22:18:10 +0800 Subject: [PATCH] models now handle authentication --- .../AddFeedWranglerViewModel.swift | 69 ++++++++++ .../AddFeedbinViewModel.swift | 67 ++++++++++ .../AddFeedlyViewModel.swift | 0 .../AddNewsBlurViewModel.swift | 70 ++++++++++ .../AddReaderAPIViewModel.swift | 121 ++++++++++++++++++ .../AddCloudKitAccountView.swift | 62 ++++++++- .../AddFeedWranglerAccountView.swift | 55 +------- .../AddFeedbinAccountView.swift | 96 +++++++------- .../AddFeedlyAccountView.swift | 0 .../AddLocalAccountView.swift | 63 ++++++++- .../AddNewsBlurAccountView.swift | 98 +++++++------- .../AddReaderAPIAccountView.swift | 102 +-------------- .../Add Account Sheets/Authentication.swift | 0 .../Accounts/SettingsAddAccountView.swift | 2 +- .../AddFeedWranglerViewModel.swift | 21 --- .../AddFeedbinViewModel.swift | 21 --- .../AddNewsBlurViewModel.swift | 21 --- .../AddReaderAPIViewModel.swift | 22 ---- NetNewsWire.xcodeproj/project.pbxproj | 30 ++++- 19 files changed, 595 insertions(+), 325 deletions(-) create mode 100644 Multiplatform/Shared/Add/Add Account Models/AddFeedWranglerViewModel.swift create mode 100644 Multiplatform/Shared/Add/Add Account Models/AddFeedbinViewModel.swift rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Models/AddFeedlyViewModel.swift (100%) create mode 100644 Multiplatform/Shared/Add/Add Account Models/AddNewsBlurViewModel.swift create mode 100644 Multiplatform/Shared/Add/Add Account Models/AddReaderAPIViewModel.swift rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddCloudKitAccountView.swift (56%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddFeedWranglerAccountView.swift (64%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddFeedbinAccountView.swift (65%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddFeedlyAccountView.swift (100%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddLocalAccountView.swift (59%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddNewsBlurAccountView.swift (64%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/AddReaderAPIAccountView.swift (57%) rename Multiplatform/{macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account => Shared/Add}/Add Account Sheets/Authentication.swift (100%) delete mode 100644 Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedWranglerViewModel.swift delete mode 100644 Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedbinViewModel.swift delete mode 100644 Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddNewsBlurViewModel.swift delete mode 100644 Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddReaderAPIViewModel.swift diff --git a/Multiplatform/Shared/Add/Add Account Models/AddFeedWranglerViewModel.swift b/Multiplatform/Shared/Add/Add Account Models/AddFeedWranglerViewModel.swift new file mode 100644 index 000000000..8f3386df7 --- /dev/null +++ b/Multiplatform/Shared/Add/Add Account Models/AddFeedWranglerViewModel.swift @@ -0,0 +1,69 @@ +// +// AddFeedWranglerViewModel.swift +// Multiplatform macOS +// +// Created by Stuart Breckenridge on 05/12/2020. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import RSCore +import RSWeb +import Secrets + +class AddFeedWranglerViewModel: ObservableObject { + @Published var isAuthenticating: Bool = false + @Published var accountUpdateError: AccountUpdateErrors = .none + @Published var showError: Bool = false + @Published var username: String = "" + @Published var password: String = "" + @Published var canDismiss: Bool = false + + func authenticateFeedWrangler() { + + isAuthenticating = true + let credentials = Credentials(type: .feedWranglerBasic, username: username, secret: password) + + Account.validateCredentials(type: .feedWrangler, credentials: credentials) { result in + + + self.isAuthenticating = false + + switch result { + case .success(let validatedCredentials): + + guard let validatedCredentials = validatedCredentials else { + self.accountUpdateError = .invalidUsernamePassword + self.showError = true + return + } + + let account = AccountManager.shared.createAccount(type: .feedWrangler) + + do { + try account.removeCredentials(type: .feedWranglerBasic) + try account.removeCredentials(type: .feedWranglerToken) + try account.storeCredentials(credentials) + try account.storeCredentials(validatedCredentials) + self.canDismiss = true + account.refreshAll(completion: { result in + switch result { + case .success: + break + case .failure(let error): + self.accountUpdateError = .other(error: error) + self.showError = true + } + }) + } catch { + self.accountUpdateError = .keyChainError + self.showError = true + } + case .failure: + self.accountUpdateError = .networkError + self.showError = true + } + } + } +} diff --git a/Multiplatform/Shared/Add/Add Account Models/AddFeedbinViewModel.swift b/Multiplatform/Shared/Add/Add Account Models/AddFeedbinViewModel.swift new file mode 100644 index 000000000..5edaed65b --- /dev/null +++ b/Multiplatform/Shared/Add/Add Account Models/AddFeedbinViewModel.swift @@ -0,0 +1,67 @@ +// +// AddFeedbinViewModel.swift +// Multiplatform macOS +// +// Created by Stuart Breckenridge on 05/12/2020. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import RSCore +import RSWeb +import Secrets + +class AddFeedbinViewModel: ObservableObject { + @Published var isAuthenticating: Bool = false + @Published var accountUpdateError: AccountUpdateErrors = .none + @Published var showError: Bool = false + @Published var username: String = "" + @Published var password: String = "" + @Published var canDismiss: Bool = false + + func authenticateFeedbin() { + isAuthenticating = true + let credentials = Credentials(type: .basic, username: username, secret: password) + + Account.validateCredentials(type: .feedbin, credentials: credentials) { result in + self.isAuthenticating = false + + switch result { + case .success(let validatedCredentials): + + guard let validatedCredentials = validatedCredentials else { + self.accountUpdateError = .invalidUsernamePassword + self.showError = true + return + } + + let account = AccountManager.shared.createAccount(type: .feedbin) + + do { + try account.removeCredentials(type: .basic) + try account.storeCredentials(validatedCredentials) + self.isAuthenticating = false + self.canDismiss = true + account.refreshAll(completion: { result in + switch result { + case .success: + break + case .failure(let error): + self.accountUpdateError = .other(error: error) + self.showError = true + } + }) + + } catch { + self.accountUpdateError = .keyChainError + self.showError = true + } + + case .failure: + self.accountUpdateError = .networkError + self.showError = true + } + } + } +} diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedlyViewModel.swift b/Multiplatform/Shared/Add/Add Account Models/AddFeedlyViewModel.swift similarity index 100% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedlyViewModel.swift rename to Multiplatform/Shared/Add/Add Account Models/AddFeedlyViewModel.swift diff --git a/Multiplatform/Shared/Add/Add Account Models/AddNewsBlurViewModel.swift b/Multiplatform/Shared/Add/Add Account Models/AddNewsBlurViewModel.swift new file mode 100644 index 000000000..df79e9965 --- /dev/null +++ b/Multiplatform/Shared/Add/Add Account Models/AddNewsBlurViewModel.swift @@ -0,0 +1,70 @@ +// +// AddNewsBlurViewModel.swift +// Multiplatform macOS +// +// Created by Stuart Breckenridge on 05/12/2020. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import RSCore +import RSWeb +import Secrets + +class AddNewsBlurViewModel: ObservableObject { + @Published var isAuthenticating: Bool = false + @Published var accountUpdateError: AccountUpdateErrors = .none + @Published var showError: Bool = false + @Published var username: String = "" + @Published var password: String = "" + @Published var canDismiss: Bool = false + + func authenticateNewsBlur() { + isAuthenticating = true + let credentials = Credentials(type: .newsBlurBasic, username: username, secret: password) + + Account.validateCredentials(type: .newsBlur, credentials: credentials) { result in + + self.isAuthenticating = false + + switch result { + case .success(let validatedCredentials): + + guard let validatedCredentials = validatedCredentials else { + self.accountUpdateError = .invalidUsernamePassword + self.showError = true + return + } + + let account = AccountManager.shared.createAccount(type: .newsBlur) + + do { + try account.removeCredentials(type: .newsBlurBasic) + try account.removeCredentials(type: .newsBlurSessionId) + try account.storeCredentials(credentials) + try account.storeCredentials(validatedCredentials) + self.canDismiss = true + account.refreshAll(completion: { result in + switch result { + case .success: + break + case .failure(let error): + self.accountUpdateError = .other(error: error) + self.showError = true + } + }) + + } catch { + self.accountUpdateError = .keyChainError + self.showError = true + } + + case .failure: + self.accountUpdateError = .networkError + self.showError = true + } + } + } + +} diff --git a/Multiplatform/Shared/Add/Add Account Models/AddReaderAPIViewModel.swift b/Multiplatform/Shared/Add/Add Account Models/AddReaderAPIViewModel.swift new file mode 100644 index 000000000..3a5af1f7e --- /dev/null +++ b/Multiplatform/Shared/Add/Add Account Models/AddReaderAPIViewModel.swift @@ -0,0 +1,121 @@ +// +// AddReaderAPIViewModel.swift +// Multiplatform macOS +// +// Created by Stuart Breckenridge on 05/12/2020. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import RSCore +import RSWeb +import Secrets + +class AddReaderAPIViewModel: ObservableObject { + @Published var isAuthenticating: Bool = false + @Published var accountUpdateError: AccountUpdateErrors = .none + @Published var showError: Bool = false + @Published var username: String = "" + @Published var password: String = "" + @Published var apiUrl: String = "" + @Published var canDismiss: Bool = false + + func authenticateReaderAccount(_ accountType: AccountType) { + isAuthenticating = true + + let credentials = Credentials(type: .readerBasic, username: username, secret: password) + + if accountType == .freshRSS { + Account.validateCredentials(type: accountType, credentials: credentials, endpoint: URL(string: apiUrl)!) { result in + + self.isAuthenticating = false + + switch result { + case .success(let validatedCredentials): + + guard let validatedCredentials = validatedCredentials else { + self.accountUpdateError = .invalidUsernamePassword + self.showError = true + return + } + + let account = AccountManager.shared.createAccount(type: .freshRSS) + + do { + try account.removeCredentials(type: .readerBasic) + try account.removeCredentials(type: .readerAPIKey) + try account.storeCredentials(credentials) + try account.storeCredentials(validatedCredentials) + self.canDismiss = true + account.refreshAll(completion: { result in + switch result { + case .success: + break + case .failure(let error): + self.accountUpdateError = .other(error: error) + self.showError = true + } + }) + + } catch { + self.accountUpdateError = .keyChainError + self.showError = true + } + + case .failure: + self.accountUpdateError = .networkError + self.showError = true + } + } + } + + else { + + Account.validateCredentials(type: accountType, credentials: credentials) { result in + + self.isAuthenticating = false + + switch result { + case .success(let validatedCredentials): + + guard let validatedCredentials = validatedCredentials else { + self.accountUpdateError = .invalidUsernamePassword + self.showError = true + return + } + + let account = AccountManager.shared.createAccount(type: .freshRSS) + + do { + try account.removeCredentials(type: .readerBasic) + try account.removeCredentials(type: .readerAPIKey) + try account.storeCredentials(credentials) + try account.storeCredentials(validatedCredentials) + self.canDismiss = true + account.refreshAll(completion: { result in + switch result { + case .success: + break + case .failure(let error): + self.accountUpdateError = .other(error: error) + self.showError = true + } + }) + + } catch { + self.accountUpdateError = .keyChainError + self.showError = true + } + + case .failure: + self.accountUpdateError = .networkError + self.showError = true + } + } + + } + + } + +} diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddCloudKitAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddCloudKitAccountView.swift similarity index 56% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddCloudKitAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddCloudKitAccountView.swift index 76d8bc25a..2db283e5b 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddCloudKitAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddCloudKitAccountView.swift @@ -14,6 +14,46 @@ struct AddCloudKitAccountView: View { @Environment (\.presentationMode) var presentationMode var body: some View { + + #if os(macOS) + macBody + #else + iosBody + #endif + + } + + #if os(iOS) + var iosBody: some View { + List { + Section(header: formHeader, content: { + Button(action: { + _ = AccountManager.shared.createAccount(type: .cloudKit) + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Add") + }) + }) + }.navigationBarItems(leading: + Button(action: { + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Dismiss") + }) + + , trailing: + Button(action: { + _ = AccountManager.shared.createAccount(type: .cloudKit) + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Add") + }) + ) + } + #endif + + #if os(macOS) + var macBody: some View { VStack { HStack(spacing: 16) { VStack(alignment: .leading) { @@ -43,6 +83,7 @@ struct AddCloudKitAccountView: View { }).keyboardShortcut(.cancelAction) Button(action: { + _ = AccountManager.shared.createAccount(type: .cloudKit) presentationMode.wrappedValue.dismiss() }, label: { Text("Create") @@ -56,7 +97,26 @@ struct AddCloudKitAccountView: View { } .padding() .frame(minWidth: 400, maxWidth: 400, maxHeight: 150) - } + } + #endif + + var formHeader: some View { + HStack { + VStack(alignment: .center) { + AccountType.cloudKit.image() + .resizable() + .frame(width: 50, height: 50) + Text("Sign in to your iCloud account.") + .font(.headline) + + Text("This account syncs across your Mac and iOS devices using your iCloud account.") + .foregroundColor(.secondary) + .font(.callout) + .lineLimit(2) + .padding(.top, 4) + } + } + } } struct AddCloudKitAccountView_Previews: PreviewProvider { diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedWranglerAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddFeedWranglerAccountView.swift similarity index 64% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedWranglerAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddFeedWranglerAccountView.swift index ebb096ff1..8e28df5a7 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedWranglerAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddFeedWranglerAccountView.swift @@ -71,7 +71,7 @@ struct AddFeedWranglerAccountView: View { }).keyboardShortcut(.cancelAction) Button(action: { - authenticateFeedWrangler() + model.authenticateFeedWrangler() }, label: { Text("Sign In") .frame(width: 60) @@ -88,56 +88,15 @@ struct AddFeedWranglerAccountView: View { .alert(isPresented: $model.showError, content: { Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel()) }) + .onReceive(model.$canDismiss, perform: { value in + if value == true { + presentationMode.wrappedValue.dismiss() + } + }) } - private func authenticateFeedWrangler() { - - model.isAuthenticating = true - let credentials = Credentials(type: .feedWranglerBasic, username: model.username, secret: model.password) - - Account.validateCredentials(type: .feedWrangler, credentials: credentials) { result in - - - self.model.isAuthenticating = false - - switch result { - case .success(let validatedCredentials): - - guard let validatedCredentials = validatedCredentials else { - self.model.accountUpdateError = .invalidUsernamePassword - self.model.showError = true - return - } - - let account = AccountManager.shared.createAccount(type: .feedWrangler) - - do { - try account.removeCredentials(type: .feedWranglerBasic) - try account.removeCredentials(type: .feedWranglerToken) - try account.storeCredentials(credentials) - try account.storeCredentials(validatedCredentials) - account.refreshAll(completion: { result in - switch result { - case .success: - self.presentationMode.wrappedValue.dismiss() - case .failure(let error): - self.model.accountUpdateError = .other(error: error) - self.model.showError = true - } - }) - - } catch { - self.model.accountUpdateError = .keyChainError - self.model.showError = true - } - - case .failure: - self.model.accountUpdateError = .networkError - self.model.showError = true - } - } - } + } struct AddFeedWranglerAccountView_Previews: PreviewProvider { diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedbinAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddFeedbinAccountView.swift similarity index 65% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedbinAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddFeedbinAccountView.swift index 71710ac78..e0f2b2884 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedbinAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddFeedbinAccountView.swift @@ -18,12 +18,45 @@ struct AddFeedbinAccountView: View { @StateObject private var model = AddFeedbinViewModel() var body: some View { + #if os(macOS) + macBody + #else + iosBody + #endif + } + + #if os(iOS) + var iosBody: some View { + List { + Section(header: formHeader, footer: ProgressView() + .scaleEffect(CGSize(width: 0.5, height: 0.5)) + .hidden(!model.isAuthenticating) , content: { + TextField("me@email.com", text: $model.username) + SecureField("•••••••••••", text: $model.password) + }) + }.navigationBarItems(leading: + Button(action: { + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Dismiss") + }) + , trailing: + Button(action: { + model.authenticateFeedbin() + }, label: { + Text("Add") + }).disabled(model.username.isEmpty || model.password.isEmpty) + ) + } + #endif + + #if os(macOS) + var macBody: some View { VStack { HStack(spacing: 16) { VStack(alignment: .leading) { AccountType.feedbin.image() .frame(width: 50, height: 50) - Spacer() } VStack(alignment: .leading, spacing: 8) { @@ -70,14 +103,13 @@ struct AddFeedbinAccountView: View { }).keyboardShortcut(.cancelAction) Button(action: { - authenticateFeedbin() + model.authenticateFeedbin() }, label: { Text("Sign In") .frame(width: 60) }) .keyboardShortcut(.defaultAction) .disabled(model.username.isEmpty || model.password.isEmpty) - } } } @@ -88,51 +120,27 @@ struct AddFeedbinAccountView: View { .alert(isPresented: $model.showError, content: { Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel()) }) - } + .onReceive(model.$canDismiss, perform: { value in + if value == true { + presentationMode.wrappedValue.dismiss() + } + }) + } + #endif - private func authenticateFeedbin() { - model.isAuthenticating = true - let credentials = Credentials(type: .basic, username: model.username, secret: model.password) - - Account.validateCredentials(type: .feedbin, credentials: credentials) { result in - self.model.isAuthenticating = false - - switch result { - case .success(let validatedCredentials): - - guard let validatedCredentials = validatedCredentials else { - self.model.accountUpdateError = .invalidUsernamePassword - self.model.showError = true - return - } - - let account = AccountManager.shared.createAccount(type: .feedbin) - - do { - try account.removeCredentials(type: .basic) - try account.storeCredentials(validatedCredentials) - self.model.isAuthenticating = false - account.refreshAll(completion: { result in - switch result { - case .success: - self.presentationMode.wrappedValue.dismiss() - case .failure(let error): - self.model.accountUpdateError = .other(error: error) - self.model.showError = true - } - }) - - } catch { - self.model.accountUpdateError = .keyChainError - self.model.showError = true - } - - case .failure: - self.model.accountUpdateError = .networkError - self.model.showError = true + var formHeader: some View { + HStack { + VStack(alignment: .center) { + AccountType.feedbin.image() + .resizable() + .frame(width: 50, height: 50) + Text("Sign in to your Feedbin account.") + .font(.headline) } } } + + } struct AddFeedbinAccountView_Previews: PreviewProvider { diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedlyAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddFeedlyAccountView.swift similarity index 100% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddFeedlyAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddFeedlyAccountView.swift diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddLocalAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddLocalAccountView.swift similarity index 59% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddLocalAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddLocalAccountView.swift index 6c0ec8d5a..a0101a5d4 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddLocalAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddLocalAccountView.swift @@ -15,7 +15,41 @@ struct AddLocalAccountView: View { @State private var newAccountName: String = "" @Environment (\.presentationMode) var presentationMode - var body: some View { + var body: some View { + #if os(macOS) + macBody + #else + iosBody + #endif + } + + #if os(iOS) + var iosBody: some View { + List { + Section(header: formHeader, content: { + TextField("Account Name", text: $newAccountName) + }) + }.navigationBarItems(leading: + Button(action: { + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Dismiss") + }) + + , trailing: + Button(action: { + let newAccount = AccountManager.shared.createAccount(type: .onMyMac) + newAccount.name = newAccountName + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Add") + }) + ) + } + #endif + + #if os(macOS) + var macBody: some View { VStack { HStack(spacing: 16) { VStack(alignment: .leading) { @@ -43,7 +77,7 @@ struct AddLocalAccountView: View { Text("Cancel") .frame(width: 60) }).keyboardShortcut(.cancelAction) - + Button(action: { let newAccount = AccountManager.shared.createAccount(type: .onMyMac) newAccount.name = newAccountName @@ -59,11 +93,28 @@ struct AddLocalAccountView: View { .padding() .frame(minWidth: 400, maxWidth: 400, minHeight: 230, maxHeight: 260) .textFieldStyle(RoundedBorderTextFieldStyle()) - } + } + #endif + + var formHeader: some View { + HStack { + VStack(alignment: .center) { + AccountType.onMyMac.image() + .resizable() + .frame(width: 50, height: 50) + Text("Create a local account on your Mac.") + .font(.headline) + Text("Local accounts store their data on your Mac. They do not sync across your devices.") + .font(.callout) + .foregroundColor(.secondary) + } + } + } + } struct AddLocalAccount_Previews: PreviewProvider { - static var previews: some View { - AddLocalAccountView() - } + static var previews: some View { + AddLocalAccountView() + } } diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddNewsBlurAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddNewsBlurAccountView.swift similarity index 64% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddNewsBlurAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddNewsBlurAccountView.swift index c2e2771ff..51ddccf90 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddNewsBlurAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddNewsBlurAccountView.swift @@ -18,6 +18,40 @@ struct AddNewsBlurAccountView: View { @StateObject private var model = AddNewsBlurViewModel() var body: some View { + #if os(macOS) + macBody + #else + iosBody + #endif + } + + #if os(iOS) + var iosBody: some View { + List { + Section(header: formHeader, footer: ProgressView() + .scaleEffect(CGSize(width: 0.5, height: 0.5)) + .hidden(!model.isAuthenticating) , content: { + TextField("me@email.com", text: $model.username) + SecureField("•••••••••••", text: $model.password) + }) + }.navigationBarItems(leading: + Button(action: { + presentationMode.wrappedValue.dismiss() + }, label: { + Text("Dismiss") + }) + , trailing: + Button(action: { + authenticateNewsBlur() + }, label: { + Text("Add") + }).disabled(model.username.isEmpty || model.password.isEmpty) + ) + } + #endif + + #if os(macOS) + var macBody: some View { VStack { HStack(spacing: 16) { VStack(alignment: .leading) { @@ -69,7 +103,7 @@ struct AddNewsBlurAccountView: View { }).keyboardShortcut(.cancelAction) Button(action: { - presentationMode.wrappedValue.dismiss() + model.authenticateNewsBlur() }, label: { Text("Sign In") .frame(width: 60) @@ -86,50 +120,28 @@ struct AddNewsBlurAccountView: View { .alert(isPresented: $model.showError, content: { Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel()) }) - } + .onReceive(model.$canDismiss, perform: { value in + if value == true { + presentationMode.wrappedValue.dismiss() + } + }) + } + #endif - private func authenticateNewsBlur() { - model.isAuthenticating = true - let credentials = Credentials(type: .newsBlurBasic, username: model.username, secret: model.password) - - Account.validateCredentials(type: .newsBlur, credentials: credentials) { result in + var formHeader: some View { + HStack { + VStack(alignment: .center) { + AccountType.newsBlur.image() + .resizable() + .frame(width: 50, height: 50) + Text("Sign in to your NewsBlur account.") + .font(.headline) - self.model.isAuthenticating = false - - switch result { - case .success(let validatedCredentials): - - guard let validatedCredentials = validatedCredentials else { - self.model.accountUpdateError = .invalidUsernamePassword - self.model.showError = true - return - } - - let account = AccountManager.shared.createAccount(type: .newsBlur) - - do { - try account.removeCredentials(type: .newsBlurBasic) - try account.removeCredentials(type: .newsBlurSessionId) - try account.storeCredentials(credentials) - try account.storeCredentials(validatedCredentials) - account.refreshAll(completion: { result in - switch result { - case .success: - self.presentationMode.wrappedValue.dismiss() - case .failure(let error): - self.model.accountUpdateError = .other(error: error) - self.model.showError = true - } - }) - - } catch { - self.model.accountUpdateError = .keyChainError - self.model.showError = true - } - - case .failure: - self.model.accountUpdateError = .networkError - self.model.showError = true + Text("This account syncs across your subscriptions across devices.") + .foregroundColor(.secondary) + .font(.callout) + .lineLimit(2) + .padding(.top, 4) } } } diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddReaderAPIAccountView.swift b/Multiplatform/Shared/Add/Add Account Sheets/AddReaderAPIAccountView.swift similarity index 57% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddReaderAPIAccountView.swift rename to Multiplatform/Shared/Add/Add Account Sheets/AddReaderAPIAccountView.swift index aac6c8991..6fa144609 100644 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/AddReaderAPIAccountView.swift +++ b/Multiplatform/Shared/Add/Add Account Sheets/AddReaderAPIAccountView.swift @@ -84,7 +84,7 @@ struct AddReaderAPIAccountView: View { }).keyboardShortcut(.cancelAction) Button(action: { - authenticateReaderAccount() + model.authenticateReaderAccount(accountType) }, label: { Text("Sign In") .frame(width: 60) @@ -101,6 +101,11 @@ struct AddReaderAPIAccountView: View { .alert(isPresented: $model.showError, content: { Alert(title: Text("Sign In Error"), message: Text(model.accountUpdateError.description), dismissButton: .cancel()) }) + .onReceive(model.$canDismiss, perform: { value in + if value == true { + presentationMode.wrappedValue.dismiss() + } + }) } func createDisabled() -> Bool { @@ -140,100 +145,7 @@ struct AddReaderAPIAccountView: View { } } - private func authenticateReaderAccount() { - model.isAuthenticating = true - - let credentials = Credentials(type: .readerBasic, username: model.username, secret: model.password) - - if accountType == .freshRSS { - Account.validateCredentials(type: accountType, credentials: credentials, endpoint: URL(string: model.apiUrl)!) { result in - - self.model.isAuthenticating = false - - switch result { - case .success(let validatedCredentials): - - guard let validatedCredentials = validatedCredentials else { - self.model.accountUpdateError = .invalidUsernamePassword - self.model.showError = true - return - } - - let account = AccountManager.shared.createAccount(type: .freshRSS) - - do { - try account.removeCredentials(type: .readerBasic) - try account.removeCredentials(type: .readerAPIKey) - try account.storeCredentials(credentials) - try account.storeCredentials(validatedCredentials) - account.refreshAll(completion: { result in - switch result { - case .success: - self.presentationMode.wrappedValue.dismiss() - case .failure(let error): - self.model.accountUpdateError = .other(error: error) - self.model.showError = true - } - }) - - } catch { - self.model.accountUpdateError = .keyChainError - self.model.showError = true - } - - case .failure: - self.model.accountUpdateError = .networkError - self.model.showError = true - } - } - } - - else { - - Account.validateCredentials(type: accountType, credentials: credentials) { result in - - self.model.isAuthenticating = false - - switch result { - case .success(let validatedCredentials): - - guard let validatedCredentials = validatedCredentials else { - self.model.accountUpdateError = .invalidUsernamePassword - self.model.showError = true - return - } - - let account = AccountManager.shared.createAccount(type: .freshRSS) - - do { - try account.removeCredentials(type: .readerBasic) - try account.removeCredentials(type: .readerAPIKey) - try account.storeCredentials(credentials) - try account.storeCredentials(validatedCredentials) - account.refreshAll(completion: { result in - switch result { - case .success: - self.presentationMode.wrappedValue.dismiss() - case .failure(let error): - self.model.accountUpdateError = .other(error: error) - self.model.showError = true - } - }) - - } catch { - self.model.accountUpdateError = .keyChainError - self.model.showError = true - } - - case .failure: - self.model.accountUpdateError = .networkError - self.model.showError = true - } - } - - } - - } + } diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/Authentication.swift b/Multiplatform/Shared/Add/Add Account Sheets/Authentication.swift similarity index 100% rename from Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Sheets/Authentication.swift rename to Multiplatform/Shared/Add/Add Account Sheets/Authentication.swift diff --git a/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift b/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift index a42caeb0f..aa7b52bc9 100644 --- a/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift +++ b/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift @@ -29,7 +29,7 @@ struct SettingsAddAccountView: View { .sheet(isPresented: $model.isAddPresented) { switch model.selectedAccountType { case .onMyMac: - SettingsLocalAccountView() + AddLocalAccountView() case .feedbin, .feedWrangler, .newsBlur, .freshRSS: SettingsCredentialsAccountView(accountType: model.selectedAccountType!) case .cloudKit: diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedWranglerViewModel.swift b/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedWranglerViewModel.swift deleted file mode 100644 index 094f0d091..000000000 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedWranglerViewModel.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// AddFeedWranglerViewModel.swift -// Multiplatform macOS -// -// Created by Stuart Breckenridge on 05/12/2020. -// Copyright © 2020 Ranchero Software. All rights reserved. -// - -import SwiftUI -import Account -import RSCore -import RSWeb -import Secrets - -class AddFeedWranglerViewModel: ObservableObject { - @Published var isAuthenticating: Bool = false - @Published var accountUpdateError: AccountUpdateErrors = .none - @Published var showError: Bool = false - @Published var username: String = "" - @Published var password: String = "" -} diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedbinViewModel.swift b/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedbinViewModel.swift deleted file mode 100644 index b0e39b393..000000000 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddFeedbinViewModel.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// AddFeedbinViewModel.swift -// Multiplatform macOS -// -// Created by Stuart Breckenridge on 05/12/2020. -// Copyright © 2020 Ranchero Software. All rights reserved. -// - -import SwiftUI -import Account -import RSCore -import RSWeb -import Secrets - -class AddFeedbinViewModel: ObservableObject { - @Published var isAuthenticating: Bool = false - @Published var accountUpdateError: AccountUpdateErrors = .none - @Published var showError: Bool = false - @Published var username: String = "" - @Published var password: String = "" -} diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddNewsBlurViewModel.swift b/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddNewsBlurViewModel.swift deleted file mode 100644 index 9769afbc9..000000000 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddNewsBlurViewModel.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// AddNewsBlurViewModel.swift -// Multiplatform macOS -// -// Created by Stuart Breckenridge on 05/12/2020. -// Copyright © 2020 Ranchero Software. All rights reserved. -// - -import SwiftUI -import Account -import RSCore -import RSWeb -import Secrets - -class AddNewsBlurViewModel: ObservableObject { - @Published var isAuthenticating: Bool = false - @Published var accountUpdateError: AccountUpdateErrors = .none - @Published var showError: Bool = false - @Published var username: String = "" - @Published var password: String = "" -} diff --git a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddReaderAPIViewModel.swift b/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddReaderAPIViewModel.swift deleted file mode 100644 index 95663ad1d..000000000 --- a/Multiplatform/macOS/Preferences/Preference Panes/Accounts/Account Preferences/Add Account/Add Account Models/AddReaderAPIViewModel.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// AddReaderAPIViewModel.swift -// Multiplatform macOS -// -// Created by Stuart Breckenridge on 05/12/2020. -// Copyright © 2020 Ranchero Software. All rights reserved. -// - -import SwiftUI -import Account -import RSCore -import RSWeb -import Secrets - -class AddReaderAPIViewModel: ObservableObject { - @Published var isAuthenticating: Bool = false - @Published var accountUpdateError: AccountUpdateErrors = .none - @Published var showError: Bool = false - @Published var username: String = "" - @Published var password: String = "" - @Published var apiUrl: String = "" -} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index ff440add3..1b2bfdd7c 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -34,6 +34,19 @@ 17241280257BBF3E00ACCEBC /* AddFeedWranglerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724127F257BBF3E00ACCEBC /* AddFeedWranglerViewModel.swift */; }; 17241288257BBF7000ACCEBC /* AddNewsBlurViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17241287257BBF7000ACCEBC /* AddNewsBlurViewModel.swift */; }; 17241290257BBFAD00ACCEBC /* AddReaderAPIViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724128F257BBFAD00ACCEBC /* AddReaderAPIViewModel.swift */; }; + 1724129D257BC01C00ACCEBC /* AddNewsBlurViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17241287257BBF7000ACCEBC /* AddNewsBlurViewModel.swift */; }; + 1724129E257BC01C00ACCEBC /* AddReaderAPIViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724128F257BBFAD00ACCEBC /* AddReaderAPIViewModel.swift */; }; + 1724129F257BC01C00ACCEBC /* AddFeedlyAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17241248257B8A8A00ACCEBC /* AddFeedlyAccountView.swift */; }; + 172412A0257BC01C00ACCEBC /* AddFeedWranglerAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF98E2AF2578AA5C00F18944 /* AddFeedWranglerAccountView.swift */; }; + 172412A1257BC01C00ACCEBC /* AddReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF98E2C52578AD1B00F18944 /* AddReaderAPIAccountView.swift */; }; + 172412A2257BC01C00ACCEBC /* AddLocalAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17386B792577C4BF0014C8B2 /* AddLocalAccountView.swift */; }; + 172412A3257BC01C00ACCEBC /* AddNewsBlurAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF98E2BD2578AC0000F18944 /* AddNewsBlurAccountView.swift */; }; + 172412A4257BC01C00ACCEBC /* AddFeedlyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17241277257BBEE700ACCEBC /* AddFeedlyViewModel.swift */; }; + 172412A5257BC01C00ACCEBC /* AddCloudKitAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF98E2992578A73A00F18944 /* AddCloudKitAccountView.swift */; }; + 172412A6257BC01C00ACCEBC /* AddFeedbinViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17241269257BBEBB00ACCEBC /* AddFeedbinViewModel.swift */; }; + 172412A7257BC01C00ACCEBC /* AddFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17386BC32577CC600014C8B2 /* AddFeedbinAccountView.swift */; }; + 172412A8257BC01C00ACCEBC /* AddFeedWranglerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1724127F257BBF3E00ACCEBC /* AddFeedWranglerViewModel.swift */; }; + 172412AF257BC0C300ACCEBC /* AccountType+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173A64162547BE0900267F6E /* AccountType+Helpers.swift */; }; 1727B39924C1368D00A4DBDC /* LayoutPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1727B39824C1368D00A4DBDC /* LayoutPreferencesView.swift */; }; 1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */; }; 1729529424AA1CAA00D65E66 /* AdvancedPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */; }; @@ -2259,8 +2272,6 @@ isa = PBXGroup; children = ( 1769E32424BC5A65000E1E8E /* AddAccountView.swift */, - 17241268257BBE7B00ACCEBC /* Add Account Models */, - 17386B812577C4C60014C8B2 /* Add Account Sheets */, ); path = "Add Account"; sourceTree = ""; @@ -2304,6 +2315,8 @@ 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */, FA80C13D24B072AA00974098 /* AddFolderModel.swift */, FA80C11624B0728000974098 /* AddFolderView.swift */, + 17241268257BBE7B00ACCEBC /* Add Account Models */, + 17386B812577C4C60014C8B2 /* Add Account Sheets */, ); path = Add; sourceTree = ""; @@ -4463,6 +4476,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 172412A6257BC01C00ACCEBC /* AddFeedbinViewModel.swift in Sources */, 17D5F17124B0BC6700375168 /* SidebarToolbarModel.swift in Sources */, 51E4995924A873F900B667CB /* ErrorHandler.swift in Sources */, 51392D1B24AC19A000BE0D35 /* SidebarExpandedContainers.swift in Sources */, @@ -4481,11 +4495,13 @@ 51E4997224A8784300B667CB /* DefaultFeedsImporter.swift in Sources */, 1704053424E5985A00A00787 /* SceneNavigationModel.swift in Sources */, 514E6C0924AD39AD00AC6F6E /* ArticleIconImageLoader.swift in Sources */, + 172412A2257BC01C00ACCEBC /* AddLocalAccountView.swift in Sources */, 6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */, FA80C13E24B072AA00974098 /* AddFolderModel.swift in Sources */, 5177470624B2910300EB0F74 /* ArticleToolbarModifier.swift in Sources */, 51919FAF24AA8EFA00541E64 /* SidebarItemView.swift in Sources */, 514E6BDA24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */, + 172412A1257BC01C00ACCEBC /* AddReaderAPIAccountView.swift in Sources */, 5177471624B37D9700EB0F74 /* ArticleIconSchemeHandler.swift in Sources */, FA80C11724B0728000974098 /* AddFolderView.swift in Sources */, 51E4990D24A808C500B667CB /* RSHTMLMetadata+Extension.swift in Sources */, @@ -4522,6 +4538,7 @@ 51E4996924A8760C00B667CB /* ArticleStylesManager.swift in Sources */, 5177471E24B387E100EB0F74 /* ImageTransition.swift in Sources */, 51E498F324A8085D00B667CB /* PseudoFeed.swift in Sources */, + 172412A5257BC01C00ACCEBC /* AddCloudKitAccountView.swift in Sources */, 65ACE48424B4779B003AE06A /* SettingsAddAccountView.swift in Sources */, 51A5769624AE617200078888 /* ArticleContainerView.swift in Sources */, 5181C5AD24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */, @@ -4531,6 +4548,7 @@ 51E4991D24A8092100B667CB /* NSAttributedString+NetNewsWire.swift in Sources */, 65082A2F24C72AC8009FA994 /* SettingsCredentialsAccountView.swift in Sources */, 51A8FFED24CA0CF400F41F1D /* WIthLatestFrom.swift in Sources */, + 1724129E257BC01C00ACCEBC /* AddReaderAPIViewModel.swift in Sources */, 51E499FD24A9137600B667CB /* SidebarModel.swift in Sources */, 5181C66224B0C326002E0F70 /* SettingsModel.swift in Sources */, 51E4995324A8734D00B667CB /* RedditFeedProvider-Extensions.swift in Sources */, @@ -4539,6 +4557,7 @@ 172199C924AB228900A31D04 /* SettingsView.swift in Sources */, 51B8BCC224C25C3E00360B00 /* SidebarContextMenu.swift in Sources */, 51A8005124CC453C00F41F1D /* ReplaySubject.swift in Sources */, + 172412A7257BC01C00ACCEBC /* AddFeedbinAccountView.swift in Sources */, 17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */, 51B80EB824BD1F8B00C6C32D /* ActivityViewController.swift in Sources */, 51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */, @@ -4556,6 +4575,7 @@ 51E4993124A8676400B667CB /* FetchRequestOperation.swift in Sources */, 51E4992624A80AAB00B667CB /* AppAssets.swift in Sources */, 514E6C0624AD2B5F00AC6F6E /* Image-Extensions.swift in Sources */, + 172412AF257BC0C300ACCEBC /* AccountType+Helpers.swift in Sources */, 51E4995624A8734D00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */, 5125E6CA24AE461D002A7562 /* TimelineLayoutView.swift in Sources */, 51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */, @@ -4566,8 +4586,10 @@ 5177470E24B2FF6F00EB0F74 /* ArticleView.swift in Sources */, 5171B4F624B7BABA00FB8D3B /* MarkStatusCommand.swift in Sources */, 65422D1724B75CD1008A2FA2 /* SettingsAddAccountModel.swift in Sources */, + 172412A3257BC01C00ACCEBC /* AddNewsBlurAccountView.swift in Sources */, 5177471424B37D4000EB0F74 /* PreloadedWebView.swift in Sources */, 51B80EDD24BD296700C6C32D /* ArticleActivityItemSource.swift in Sources */, + 1724129D257BC01C00ACCEBC /* AddNewsBlurViewModel.swift in Sources */, 17897ACA24C281A40014BA03 /* InspectorView.swift in Sources */, 517B2EBC24B3E62A001AC46C /* WrapperScriptMessageHandler.swift in Sources */, 51919FB324AAB97900541E64 /* FeedIconImageLoader.swift in Sources */, @@ -4587,6 +4609,7 @@ 5177471A24B3863000EB0F74 /* WebViewProvider.swift in Sources */, 51E4992124A8095000B667CB /* RSImage-Extensions.swift in Sources */, 51A8001224CA0FC700F41F1D /* Sink.swift in Sources */, + 172412A8257BC01C00ACCEBC /* AddFeedWranglerViewModel.swift in Sources */, 51E4990324A808BB00B667CB /* FaviconDownloader.swift in Sources */, 172199ED24AB2E0100A31D04 /* SafariView.swift in Sources */, 65ACE48624B477C9003AE06A /* SettingsAccountLabelView.swift in Sources */, @@ -4601,14 +4624,17 @@ 51C0515E24A77DF800194D5E /* MainApp.swift in Sources */, 51919FF724AB8B7700541E64 /* TimelineView.swift in Sources */, 51E4993D24A870F800B667CB /* UserNotificationManager.swift in Sources */, + 172412A0257BC01C00ACCEBC /* AddFeedWranglerAccountView.swift in Sources */, 5177470324B2657F00EB0F74 /* TimelineToolbarModifier.swift in Sources */, 51B80EDF24BD298900C6C32D /* TitleActivityItemSource.swift in Sources */, 51E4991524A808FF00B667CB /* ArticleStringFormatter.swift in Sources */, 51919FEE24AB85E400541E64 /* TimelineContainerView.swift in Sources */, 653A4E7924BCA5BB00EF2D7F /* SettingsCloudKitAccountView.swift in Sources */, + 1724129F257BC01C00ACCEBC /* AddFeedlyAccountView.swift in Sources */, 51E4995724A8734D00B667CB /* ExtensionPoint.swift in Sources */, 51A8002D24CC451500F41F1D /* ShareReplay.swift in Sources */, 51B8BCE624C25F7C00360B00 /* TimelineContextMenu.swift in Sources */, + 172412A4257BC01C00ACCEBC /* AddFeedlyViewModel.swift in Sources */, 1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */, 51E4991124A808DE00B667CB /* SmallIconProvider.swift in Sources */, );