Update Settings for Accounts to work with the latest SwiftUI

This commit is contained in:
Maurice Parker 2019-09-07 20:50:57 -05:00
parent a01b9ebe73
commit fe874f3ca7
8 changed files with 215 additions and 170 deletions

@ -34,6 +34,8 @@
5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */; }; 5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */; };
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7C8223205EFB00BAC947 /* RootSplitViewController.swift */; }; 514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7C8223205EFB00BAC947 /* RootSplitViewController.swift */; };
514B7D1F23219F3C00BAC947 /* AddControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7D1E23219F3C00BAC947 /* AddControllerType.swift */; }; 514B7D1F23219F3C00BAC947 /* AddControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7D1E23219F3C00BAC947 /* AddControllerType.swift */; };
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */; };
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */; };
51543685228F6753005E1CDF /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51543684228F6753005E1CDF /* DetailAccountViewController.swift */; }; 51543685228F6753005E1CDF /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51543684228F6753005E1CDF /* DetailAccountViewController.swift */; };
515436882291D75D005E1CDF /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */; }; 515436882291D75D005E1CDF /* AddLocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */; };
5154368A2291FED9005E1CDF /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */; }; 5154368A2291FED9005E1CDF /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */; };
@ -64,6 +66,10 @@
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; }; 519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; };
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; }; 519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; };
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; }; 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */; };
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */; };
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */; };
51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51AF460D232488C6001742EF /* Account-Extensions.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; }; 51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51C451B9226377C900C03939 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 840716732262A60F00344432 /* Articles.framework */; }; 51C451B9226377C900C03939 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 840716732262A60F00344432 /* Articles.framework */; };
@ -732,6 +738,7 @@
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = "<group>"; }; 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = "<group>"; };
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; }; 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = "<group>"; };
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; };
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; };
51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; }; 51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; };
@ -1137,6 +1144,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */, 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */,
51AF460D232488C6001742EF /* Account-Extensions.swift */,
); );
path = "Model Extensions"; path = "Model Extensions";
sourceTree = "<group>"; sourceTree = "<group>";
@ -2443,6 +2451,7 @@
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */, 5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */, 51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */, 51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */,
515ADE4022E11FAE006B2460 /* SystemMessageViewController.swift in Sources */, 515ADE4022E11FAE006B2460 /* SystemMessageViewController.swift in Sources */,
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */, 51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */, 51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
@ -2451,12 +2460,15 @@
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */, 51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */, 51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */,
51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */, 51C4528F226509BD00C03939 /* UnreadFeed.swift in Sources */,
51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */,
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */, 5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */,
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */,
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */, 51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */,
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */, 51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */,
51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */, 51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */,
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */, 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */,
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */, 514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */,
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */,
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */, 51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */, 51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */,
51C452852265093600C03939 /* AddFeedFolderPickerData.swift in Sources */, 51C452852265093600C03939 /* AddFeedFolderPickerData.swift in Sources */,
@ -2469,6 +2481,7 @@
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */, 519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */, 5183CCEF227125970010922C /* SettingsViewController.swift in Sources */,
51F85BE5227217D000C787DC /* RefreshIntervalViewController.swift in Sources */, 51F85BE5227217D000C787DC /* RefreshIntervalViewController.swift in Sources */,
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */,
51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */, 51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */,
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */, 519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */,
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */, 51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
@ -2506,6 +2519,7 @@
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */, 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
51C45290226509C100C03939 /* PseudoFeed.swift in Sources */, 51C45290226509C100C03939 /* PseudoFeed.swift in Sources */,
51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */, 51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */,
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */,
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */, 5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */,
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */, 51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */,
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */, 512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */,

@ -0,0 +1,16 @@
//
// Account-Extensions.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 9/7/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import Account
extension Account: Identifiable {
public var id: String {
return accountID
}
}

@ -14,19 +14,13 @@ struct SettingsAccountLabelView : View {
var body: some View { var body: some View {
HStack { HStack {
Spacer() Image(accountImage)
HStack { .resizable()
Image(accountImage) .aspectRatio(1, contentMode: .fit)
.resizable() .frame(height: 32)
.aspectRatio(1, contentMode: .fit) Text(verbatim: accountLabel).font(.title)
.frame(height: 32)
Text(verbatim: accountLabel).font(.title)
}
.layoutPriority(1)
Spacer()
} }
.foregroundColor(.primary) .foregroundColor(.primary).padding(4.0)
} }
} }

@ -10,17 +10,38 @@ import SwiftUI
import Account import Account
struct SettingsAddAccountView : View { struct SettingsAddAccountView : View {
@State private var isAddPresented = false
@State private var selectedAccountType: AccountType = nil
var body: some View { var body: some View {
Form { Form {
PresentationButton(destination: SettingsLocalAccountView(name: "")) {
Button(action: {
self.selectedAccountType = AccountType.onMyMac
self.isAddPresented.toggle()
}) {
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName) SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
} }
.padding(4)
PresentationButton(destination: SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())) { Button(action: {
self.selectedAccountType = AccountType.feedbin
self.isAddPresented.toggle()
}) {
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin") SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
} }
.padding(4)
} }
.sheet(isPresented: $isAddPresented) {
if self.selectedAccountType == .onMyMac {
SettingsLocalAccountView(name: "")
}
if self.selectedAccountType == .feedbin {
SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
}
}
.navigationBarTitle(Text("Add Account"), displayMode: .inline) .navigationBarTitle(Text("Add Account"), displayMode: .inline)
} }
} }

@ -12,17 +12,15 @@ import Account
import RSWeb import RSWeb
struct SettingsDetailAccountView : View { struct SettingsDetailAccountView : View {
@ObjectBinding var viewModel: ViewModel @ObservedObject var viewModel: ViewModel
@State private var verifyDelete = false @State private var isFeedbinCredentialsPresented = false
@State private var showFeedbinCredentials = false @State private var isDeleteAlertPresented = false
var body: some View { var body: some View {
Form { Form {
Section { Section {
HStack { HStack {
Text("Name") TextField("Name", text: $viewModel.name)
Divider()
TextField($viewModel.name, placeholder: Text("(Optional)"))
} }
Toggle(isOn: $viewModel.isActive) { Toggle(isOn: $viewModel.isActive) {
Text("Active") Text("Active")
@ -33,33 +31,33 @@ struct SettingsDetailAccountView : View {
HStack { HStack {
Spacer() Spacer()
Button(action: { Button(action: {
self.showFeedbinCredentials = true self.isFeedbinCredentialsPresented.toggle()
}) { }) {
Text("Credentials") Text("Credentials")
} }
.presentation(showFeedbinCredentials ? feedbinCredentialsModal : nil)
.onDisappear() { self.showFeedbinCredentials = false }
Spacer() Spacer()
} }
} }
.sheet(isPresented: $isFeedbinCredentialsPresented) {
self.settingsFeedbinAccountView
}
} }
if viewModel.isDeletable { if viewModel.isDeletable {
Section { Section {
HStack { HStack {
Spacer() Spacer()
Button(action: { Button(action: {
self.verifyDelete = true self.isDeleteAlertPresented.toggle()
}) { }) {
Text("Delete Account") Text("Delete Account").foregroundColor(.red)
.foregroundColor(.red)
}
.presentation($verifyDelete) {
Alert(title: Text("Are you sure you want to delete \"\(viewModel.nameForDisplay)\"?"),
primaryButton: Alert.Button.default(Text("Delete"), onTrigger: { self.viewModel.delete() }),
secondaryButton: Alert.Button.cancel())
} }
Spacer() Spacer()
} }
.alert(isPresented: $isDeleteAlertPresented) {
Alert(title: Text("Are you sure you want to delete \"\(viewModel.nameForDisplay)\"?"),
primaryButton: Alert.Button.default(Text("Delete"), action: { self.viewModel.delete() }),
secondaryButton: Alert.Button.cancel())
}
} }
} }
} }
@ -67,13 +65,15 @@ struct SettingsDetailAccountView : View {
} }
var feedbinCredentialsModal: Modal { var settingsFeedbinAccountView: SettingsFeedbinAccountView {
let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account) let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account)
return Modal(SettingsFeedbinAccountView(viewModel: feedbinViewModel)) return SettingsFeedbinAccountView(viewModel: feedbinViewModel)
} }
class ViewModel: BindableObject { class ViewModel: ObservableObject {
let didChange = PassthroughSubject<ViewModel, Never>()
let objectWillChange = ObservableObjectPublisher()
let account: Account let account: Account
init(_ account: Account) { init(_ account: Account) {
@ -89,8 +89,8 @@ struct SettingsDetailAccountView : View {
account.name ?? "" account.name ?? ""
} }
set { set {
objectWillChange.send()
account.name = newValue.isEmpty ? nil : newValue account.name = newValue.isEmpty ? nil : newValue
didChange.send(self)
} }
} }
@ -99,8 +99,8 @@ struct SettingsDetailAccountView : View {
account.isActive account.isActive
} }
set { set {
objectWillChange.send()
account.isActive = newValue account.isActive = newValue
didChange.send(self)
} }
} }
@ -114,7 +114,7 @@ struct SettingsDetailAccountView : View {
func delete() { func delete() {
AccountManager.shared.deleteAccount(account) AccountManager.shared.deleteAccount(account)
ActivityManager.shared.cleanUp(account) // ActivityManager.shared.cleanUp(account)
} }
} }
} }

@ -12,10 +12,10 @@ import Account
import RSWeb import RSWeb
struct SettingsFeedbinAccountView : View { struct SettingsFeedbinAccountView : View {
@Environment(\.isPresented) private var isPresented @Environment(\.presentationMode) var presentation
@ObjectBinding var viewModel: ViewModel @ObservedObject var viewModel: ViewModel
@State var busy: Bool = false @State var busy: Bool = false
@State var error: Text = Text("") @State var error: String = ""
var body: some View { var body: some View {
NavigationView { NavigationView {
@ -23,22 +23,13 @@ struct SettingsFeedbinAccountView : View {
Section(header: Section(header:
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin").padding() SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin").padding()
) { ) {
HStack { TextField("Email", text: $viewModel.email).textContentType(.emailAddress)
Text("Email:") SecureField("Password", text: $viewModel.password)
Divider()
TextField($viewModel.email)
.textContentType(.username)
}
HStack {
Text("Password:")
Divider()
SecureField($viewModel.password)
}
} }
Section(footer: Section(footer:
HStack { HStack {
Spacer() Spacer()
error.color(.red) Text(verbatim: error).foregroundColor(.red)
Spacer() Spacer()
} }
) { ) {
@ -67,7 +58,7 @@ struct SettingsFeedbinAccountView : View {
private func addAccount() { private func addAccount() {
busy = true busy = true
error = Text("") error = ""
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces) let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
let credentials = Credentials.basic(username: emailAddress, password: viewModel.password) let credentials = Credentials.basic(username: emailAddress, password: viewModel.password)
@ -104,15 +95,15 @@ struct SettingsFeedbinAccountView : View {
self.dismiss() self.dismiss()
} catch { } catch {
self.error = Text("Keychain error while storing credentials.") self.error = "Keychain error while storing credentials."
} }
} else { } else {
self.error = Text("Invalid email/password combination.") self.error = "Invalid email/password combination."
} }
case .failure: case .failure:
self.error = Text("Network error. Try again later.") self.error = "Network error. Try again later."
} }
} }
@ -120,11 +111,12 @@ struct SettingsFeedbinAccountView : View {
} }
private func dismiss() { private func dismiss() {
isPresented?.value = false presentation.wrappedValue.dismiss()
} }
class ViewModel: BindableObject { class ViewModel: ObservableObject {
let didChange = PassthroughSubject<ViewModel, Never>()
let objectWillChange = ObservableObjectPublisher()
var account: Account? = nil var account: Account? = nil
init() { init() {
@ -139,13 +131,14 @@ struct SettingsFeedbinAccountView : View {
} }
var email: String = "" { var email: String = "" {
didSet { willSet {
didChange.send(self) objectWillChange.send()
} }
} }
var password: String = "" { var password: String = "" {
didSet { willSet {
didChange.send(self) objectWillChange.send()
} }
} }

@ -10,7 +10,7 @@ import SwiftUI
import Account import Account
struct SettingsLocalAccountView : View { struct SettingsLocalAccountView : View {
@Environment(\.isPresented) private var isPresented @Environment(\.presentationMode) var presentation
@State var name: String @State var name: String
var body: some View { var body: some View {
@ -20,9 +20,7 @@ struct SettingsLocalAccountView : View {
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName).padding() SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName).padding()
) { ) {
HStack { HStack {
Text("Name") TextField("Name", text: $name)
Divider()
TextField($name, placeholder: Text("(Optional)"))
} }
} }
Section { Section {
@ -47,7 +45,7 @@ struct SettingsLocalAccountView : View {
} }
private func dismiss() { private func dismiss() {
isPresented?.value = false presentation.wrappedValue.dismiss()
} }
} }

@ -29,108 +29,117 @@ struct SettingsView : View {
var body: some View { var body: some View {
NavigationView { NavigationView {
Form { Form {
buildAccountsSection()
// Section(header: Text("ACCOUNTS")) { buildTimelineSection()
// ForEach(viewModel.accounts.identified(by: \.self)) { account in buildDatabaseSection()
// NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) { buildAboutSection()
// Text(verbatim: account.nameForDisplay)
// }
// }
// NavigationLink(destination: SettingsAddAccountView(), isDetail: false) {
// Text("Add Account")
// }
// }
Section(header: Text("TIMELINE")) {
Toggle(isOn: $viewModel.sortOldestToNewest) {
Text("Sort Oldest to Newest")
}
Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
}
}
Section(header: Text("DATABASE")) {
Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
ForEach(RefreshInterval.allCases) { interval in
Text(interval.description()).tag(interval)
}
}
VStack {
Button("Import Subscriptions...") {
self.isOPMLImportPresented = true
}
}.actionSheet(isPresented: $isOPMLImportPresented) {
createSubscriptionsImportAccounts
}.sheet(isPresented: $isOPMLImportDocPickerPresented) {
SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
}
VStack {
Button("Export Subscriptions...") {
self.isOPMLExportPresented = true
}
}.actionSheet(isPresented: $isOPMLExportPresented) {
createSubscriptionsExportAccounts
}.sheet(isPresented: $isOPMLExportDocPickerPresented) {
SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
}
}
.foregroundColor(.primary)
Section(header: Text("ABOUT"), footer: buildFooter) {
Text("About NetNewsWire")
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://ranchero.com/netnewswire/"
}) {
Text("Website")
}
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire"
}) {
Text("Github Repository")
}
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
}) {
Text("Bug Tracker")
}
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
}) {
Text("Technotes")
}
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
}) {
Text("How To Support NetNewsWire")
}
Text("Add NetNewsWire News Feed")
}.sheet(isPresented: $isWebsitePresented) {
SafariView(url: URL(string: self.website!)!)
}
.foregroundColor(.primary)
} }
.navigationBarTitle(Text("Settings"), displayMode: .inline) .navigationBarTitle(Text("Settings"), displayMode: .inline)
.navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } ) .navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
} }
} }
var createSubscriptionsImportAccounts: ActionSheet { func buildAccountsSection() -> some View {
Section(header: Text("ACCOUNTS")) {
ForEach(viewModel.accounts) { account in
NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account))) {
Text(verbatim: account.nameForDisplay)
}
}
NavigationLink(destination: SettingsAddAccountView()) {
Text("Add Account")
}
}
}
func buildTimelineSection() -> some View {
Section(header: Text("TIMELINE")) {
Toggle(isOn: $viewModel.sortOldestToNewest) {
Text("Sort Oldest to Newest")
}
Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
}
}
}
func buildDatabaseSection() -> some View {
Section(header: Text("DATABASE")) {
Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) {
ForEach(RefreshInterval.allCases) { interval in
Text(interval.description()).tag(interval)
}
}
VStack {
Button("Import Subscriptions...") {
self.isOPMLImportPresented = true
}
}.actionSheet(isPresented: $isOPMLImportPresented) {
buildSubscriptionsImportAccounts()
}.sheet(isPresented: $isOPMLImportDocPickerPresented) {
SettingsSubscriptionsImportDocumentPickerView(account: self.opmlAccount!)
}.foregroundColor(.primary)
VStack {
Button("Export Subscriptions...") {
self.isOPMLExportPresented = true
}
}.actionSheet(isPresented: $isOPMLExportPresented) {
buildSubscriptionsExportAccounts()
}.sheet(isPresented: $isOPMLExportDocPickerPresented) {
SettingsSubscriptionsExportDocumentPickerView(account: self.opmlAccount!)
}.foregroundColor(.primary)
}
}
func buildAboutSection() -> some View {
Section(header: Text("ABOUT"), footer: buildFooter()) {
Text("About NetNewsWire")
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://ranchero.com/netnewswire/"
}) {
Text("Website")
}.foregroundColor(.primary)
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire"
}) {
Text("Github Repository")
}.foregroundColor(.primary)
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
}) {
Text("Bug Tracker")
}.foregroundColor(.primary)
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
}) {
Text("Technotes")
}.foregroundColor(.primary)
Button(action: {
self.isWebsitePresented.toggle()
self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
}) {
Text("How To Support NetNewsWire")
}.foregroundColor(.primary)
Text("Add NetNewsWire News Feed")
}.sheet(isPresented: $isWebsitePresented) {
SafariView(url: URL(string: self.website!)!)
}
}
func buildSubscriptionsImportAccounts() -> ActionSheet {
var buttons = [ActionSheet.Button]() var buttons = [ActionSheet.Button]()
for account in viewModel.activeAccounts { for account in viewModel.activeAccounts {
@ -150,7 +159,7 @@ struct SettingsView : View {
return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons) return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
} }
var createSubscriptionsExportAccounts: ActionSheet { func buildSubscriptionsExportAccounts() -> ActionSheet {
var buttons = [ActionSheet.Button]() var buttons = [ActionSheet.Button]()
for account in viewModel.accounts { for account in viewModel.accounts {
@ -165,7 +174,7 @@ struct SettingsView : View {
return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
} }
var buildFooter: some View { func buildFooter() -> some View {
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))") return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
.font(.footnote) .font(.footnote)
.foregroundColor(.secondary) .foregroundColor(.secondary)