diff --git a/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift b/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift index 6bd475367..76385f098 100644 --- a/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift +++ b/Multiplatform/iOS/Settings/Accounts/SettingsAddAccountView.swift @@ -40,7 +40,7 @@ struct SettingsAddAccountView: View { SettingsLocalAccountView() } if selectedAccountType == .feedbin { - //SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel()) + SettingsFeedbinAccountView() } } .navigationBarTitle(Text("Add Account"), displayMode: .inline) diff --git a/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountModel.swift b/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountModel.swift new file mode 100644 index 000000000..0ce461b9c --- /dev/null +++ b/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountModel.swift @@ -0,0 +1,111 @@ +// +// SettingsFeedbinAccountModel.swift +// Multiplatform iOS +// +// Created by Rizwan on 08/07/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import Secrets + +enum FeedbinAccountError: LocalizedError { + + case none, keyChain, invalidCredentials, noNetwork + + var errorDescription: String? { + switch self { + case .keyChain: + return NSLocalizedString("Keychain error while storing credentials.", comment: "") + case .invalidCredentials: + return NSLocalizedString("Invalid email/password combination.", comment: "") + case .noNetwork: + return NSLocalizedString("Network error. Try again later.", comment: "") + default: + return nil + } + } + +} + +class SettingsFeedbinAccountModel: ObservableObject { + var account: Account? = nil + @Published var shouldDismiss: Bool = false + @Published var email: String = "" + @Published var password: String = "" + @Published var busy: Bool = false + @Published var feedbinAccountError: FeedbinAccountError? { + didSet { + feedbinAccountError != FeedbinAccountError.none ? (showError = true) : (showError = false) + } + } + @Published var showError: Bool = false + + init() { + + } + + init(account: Account) { + self.account = account + if let credentials = try? account.retrieveCredentials(type: .basic) { + self.email = credentials.username + self.password = credentials.secret + } + } + + var isUpdate: Bool { + return account != nil + } + + var isValid: Bool { + return !email.isEmpty && !password.isEmpty + } + + func addAccount() { + busy = true + feedbinAccountError = FeedbinAccountError.none + + let emailAddress = email.trimmingCharacters(in: .whitespaces) + let credentials = Credentials(type: .basic, username: emailAddress, secret: password) + + Account.validateCredentials(type: .feedbin, credentials: credentials) { (result) in + self.busy = false + + switch result { + case .success(let authenticated): + if (authenticated != nil) { + var newAccount = false + let workAccount: Account + if self.account == nil { + workAccount = AccountManager.shared.createAccount(type: .feedbin) + newAccount = true + } else { + workAccount = self.account! + } + + do { + do { + try workAccount.removeCredentials(type: .basic) + } catch {} + try workAccount.storeCredentials(credentials) + + if newAccount { + workAccount.refreshAll() { result in } + } + + self.shouldDismiss = true + } catch { + self.feedbinAccountError = FeedbinAccountError.keyChain + } + + } else { + self.feedbinAccountError = FeedbinAccountError.invalidCredentials + } + case .failure: + self.feedbinAccountError = FeedbinAccountError.noNetwork + } + } + } +} + diff --git a/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountView.swift b/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountView.swift new file mode 100644 index 000000000..9d320f41c --- /dev/null +++ b/Multiplatform/iOS/Settings/Accounts/SettingsFeedbinAccountView.swift @@ -0,0 +1,91 @@ +// +// SettingsFeedbinAccountView.swift +// Multiplatform iOS +// +// Created by Rizwan on 07/07/20. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account +import Combine +import RSWeb +import Secrets + +struct SettingsFeedbinAccountView: View { + @Environment(\.presentationMode) var presentationMode + @StateObject var settingsModel = SettingsFeedbinAccountModel() + + var body: some View { + NavigationView { + List { + Section { + imageView + TextField("Email", text: $settingsModel.email).textContentType(.emailAddress) + SecureField("Password", text: $settingsModel.password) + } + Section(footer: errorFooter) { + HStack { + Spacer() + Button(action: { settingsModel.addAccount() }) { + if settingsModel.isUpdate { + Text("Update Account") + } else { + Text("Add Account") + } + } + .disabled(!settingsModel.isValid) + Spacer() + if settingsModel.busy { + ProgressView() + } + } + } + } + .onReceive(settingsModel.$shouldDismiss, perform: { dismiss in + if dismiss == true { + presentationMode.wrappedValue.dismiss() + } + }) + .listStyle(InsetGroupedListStyle()) + .disabled(settingsModel.busy) + .navigationBarTitle(Text(verbatim: "Feedbin"), displayMode: .inline) + .navigationBarItems(leading: + Button(action: { self.dismiss() }) { Text("Cancel") } + ) + } + } + + var imageView: some View { + HStack { + Spacer() + Image(rsImage: AppAssets.image(for: .feedbin)!) + .resizable() + .aspectRatio(1, contentMode: .fit) + .frame(height: 48, alignment: .center) + .padding() + Spacer() + } + .listRowBackground(Color.clear) + } + + var errorFooter: some View { + HStack { + Spacer() + if settingsModel.showError { + Text(verbatim: settingsModel.feedbinAccountError!.localizedDescription).foregroundColor(.red) + } + Spacer() + } + } + + private func dismiss() { + presentationMode.wrappedValue.dismiss() + } +} + +struct SettingsFeedbinAccountView_Previews: PreviewProvider { + static var previews: some View { + SettingsFeedbinAccountView() + } +} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 29f64d84b..6471041d4 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -674,10 +674,12 @@ 6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; }; 6581C74020CED60100F4AD34 /* netnewswire-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */; }; 6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */; }; + 6591720E24B59C5100B638E8 /* SettingsFeedbinAccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6591720D24B59C5100B638E8 /* SettingsFeedbinAccountModel.swift */; }; 6594CA3B24AF6F2A005C7D7C /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; }; 65ACE48424B4779B003AE06A /* SettingsAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65ACE48324B4779B003AE06A /* SettingsAddAccountView.swift */; }; 65ACE48624B477C9003AE06A /* SettingsAccountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65ACE48524B477C9003AE06A /* SettingsAccountLabelView.swift */; }; 65ACE48824B48020003AE06A /* SettingsLocalAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65ACE48724B48020003AE06A /* SettingsLocalAccountView.swift */; }; + 65ACE48A24B4C2D8003AE06A /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65ACE48924B4C2D8003AE06A /* SettingsFeedbinAccountView.swift */; }; 65C2E40124B05D8A000AFDF6 /* FeedsSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */; }; 65CBAD5A24AE03C20006DD91 /* ColorPaletteContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */; }; 65ED3FB7235DEF6C0081F399 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; }; @@ -2088,9 +2090,11 @@ 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "netnewswire-subscribe-to-feed.js"; sourceTree = ""; }; 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = ""; }; 6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Subscribe_to_Feed.entitlements; sourceTree = ""; }; + 6591720D24B59C5100B638E8 /* SettingsFeedbinAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountModel.swift; sourceTree = ""; }; 65ACE48324B4779B003AE06A /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = ""; }; 65ACE48524B477C9003AE06A /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = ""; }; 65ACE48724B48020003AE06A /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = ""; }; + 65ACE48924B4C2D8003AE06A /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = ""; }; 65C2E40024B05D8A000AFDF6 /* FeedsSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedsSettingsModel.swift; sourceTree = ""; }; 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteContainerView.swift; sourceTree = ""; }; 65ED4083235DEF6C0081F399 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -3146,6 +3150,8 @@ 65ACE48324B4779B003AE06A /* SettingsAddAccountView.swift */, 65ACE48524B477C9003AE06A /* SettingsAccountLabelView.swift */, 65ACE48724B48020003AE06A /* SettingsLocalAccountView.swift */, + 65ACE48924B4C2D8003AE06A /* SettingsFeedbinAccountView.swift */, + 6591720D24B59C5100B638E8 /* SettingsFeedbinAccountModel.swift */, ); path = Accounts; sourceTree = ""; @@ -4986,6 +4992,7 @@ 5177471824B3812200EB0F74 /* IconView.swift in Sources */, 51E4995C24A875F300B667CB /* ArticleRenderer.swift in Sources */, 51E4992324A8095700B667CB /* URL-Extensions.swift in Sources */, + 65ACE48A24B4C2D8003AE06A /* SettingsFeedbinAccountView.swift in Sources */, 51E4993624A867E800B667CB /* UserInfoKey.swift in Sources */, 51E4990924A808C500B667CB /* WebFeedIconDownloader.swift in Sources */, 51E498F524A8085D00B667CB /* TodayFeedDelegate.swift in Sources */, @@ -5016,6 +5023,7 @@ 51E4990124A808BB00B667CB /* FaviconURLFinder.swift in Sources */, 51E4991D24A8092100B667CB /* NSAttributedString+NetNewsWire.swift in Sources */, FF64D0E924AF53EE0084080A /* RefreshProgressView.swift in Sources */, + 6591720E24B59C5100B638E8 /* SettingsFeedbinAccountModel.swift in Sources */, 51E499FD24A9137600B667CB /* SidebarModel.swift in Sources */, 517B2EEB24B40E09001AC46C /* TimelineTitleModifier.swift in Sources */, 5181C66224B0C326002E0F70 /* SettingsModel.swift in Sources */,