Spit and polish on the Accounts views

This commit is contained in:
Stuart Breckenridge
2022-12-18 17:08:03 +08:00
parent 4ed11c0fc6
commit b4cb253c66
9 changed files with 101 additions and 62 deletions

View File

@@ -12,7 +12,7 @@
"LOCAL_ACCOUNT_NAME_PHONE" = "On My iPhone";
"LOCAL_ACCOUNT_NAME_PAD" = "On My iPad";
"ACCOUNT_EMAIL_ADDRESS_PROMPT" = "Email Address";
"ACCOUNT_USERNAME_PROMPT" = "Username";
"ACCOUNT_USERNAME_OR_EMAIL_PROMPT" = "Username or Email";
"ACCOUNT_PASSWORD_PROMPT" = "Password";

View File

@@ -12,7 +12,7 @@ import Account
struct CloudKitAddAccountView: View {
@Environment(\.dismiss) private var dismiss
@State private var addAccountError: (LocalizedError?, Bool) = (nil, false)
@State private var accountError: (Error?, Bool) = (nil, false)
var body: some View {
NavigationView {
@@ -28,10 +28,10 @@ struct CloudKitAddAccountView: View {
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
}
}
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $addAccountError.1) {
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
Button(action: {}, label: { Text("DISMISS_BUTTON_TITLE", tableName: "Buttons") })
} message: {
Text(addAccountError.0?.localizedDescription ?? "Unknown Error")
Text(accountError.0?.localizedDescription ?? "Unknown Error")
}
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
@@ -41,7 +41,7 @@ struct CloudKitAddAccountView: View {
var createCloudKitAccount: some View {
Button {
guard FileManager.default.ubiquityIdentityToken != nil else {
addAccountError = (LocalizedNetNewsWireError.iCloudDriveMissing, true)
accountError = (LocalizedNetNewsWireError.iCloudDriveMissing, true)
return
}
let _ = AccountManager.shared.createAccount(type: .cloudKit)

View File

@@ -54,6 +54,7 @@ struct FeedbinAddAccountView: View {
}
.navigationTitle(Text(account?.type.localizedAccountName() ?? ""))
.navigationBarTitleDisplayMode(.inline)
.interactiveDismissDisabled(showProgressIndicator)
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
@@ -73,8 +74,14 @@ struct FeedbinAddAccountView: View {
Button {
Task {
do {
try await executeAccountCredentials()
dismiss()
if account == nil {
// Create a new account
try await executeAccountCredentials()
} else {
// Updating account credentials
try await executeAccountCredentials()
dismiss()
}
} catch {
accountError = (error, true)
}

View File

@@ -12,7 +12,7 @@ import Secrets
import RSWeb
import RSCore
struct NewsBlurAddAccountView: View {
struct NewsBlurAddAccountView: View, Logging {
@Environment(\.dismiss) private var dismiss
@State var account: Account? = nil
@@ -52,6 +52,7 @@ struct NewsBlurAddAccountView: View {
} message: {
Text(accountError.0?.localizedDescription ?? "")
}
.interactiveDismissDisabled(showProgressIndicator)
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
@@ -78,7 +79,7 @@ struct NewsBlurAddAccountView: View {
var accountDetails: some View {
Section {
TextField("Email", text: $accountUserName, prompt: Text("ACCOUNT_USERNAME_PROMPT", tableName: "Account"))
TextField("Email", text: $accountUserName, prompt: Text("ACCOUNT_USERNAME_OR_EMAIL_PROMPT", tableName: "Account"))
.autocorrectionDisabled()
.autocapitalization(.none)
SecureField("Password", text: $accountPassword, prompt: Text("ACCOUNT_PASSWORD_PROMPT", tableName: "Account"))
@@ -90,8 +91,14 @@ struct NewsBlurAddAccountView: View {
Button {
Task {
do {
try await executeAccountCredentials()
dismiss()
if account == nil {
// Create a new account
try await executeAccountCredentials()
} else {
// Updating account credentials
try await executeAccountCredentials()
dismiss()
}
} catch {
accountError = (error, true)
}
@@ -115,27 +122,33 @@ struct NewsBlurAddAccountView: View {
}
private func executeAccountCredentials() async throws {
let trimmedEmailAddress = accountUserName.trimmingWhitespace
let trimmedUsername = accountUserName.trimmingWhitespace
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedEmailAddress)) else {
guard (account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedUsername)) else {
throw LocalizedNetNewsWireError.duplicateAccount
}
showProgressIndicator = true
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedEmailAddress, secret: accountPassword)
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedUsername, secret: accountPassword)
return try await withCheckedThrowingContinuation { continuation in
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
switch result {
case .success(let credentials):
if let sessionsCredentials = credentials {
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .newsBlur)
}
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
do {
try self.account?.removeCredentials(type: .newsBlurBasic)
try self.account?.removeCredentials(type: .newsBlurSessionId)
} catch {
NewsBlurAddAccountView.logger.error("\(error.localizedDescription)")
}
try self.account?.storeCredentials(basicCredentials)
try self.account?.storeCredentials(sessionsCredentials)
@@ -144,22 +157,27 @@ struct NewsBlurAddAccountView: View {
case .success(_):
showProgressIndicator = false
continuation.resume()
return
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
return
}
})
} catch {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
return
}
} else {
showProgressIndicator = false
continuation.resume(throwing: LocalizedNetNewsWireError.invalidUsernameOrPassword)
return
}
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
return
}
}
}

View File

@@ -24,7 +24,7 @@ struct ReaderAPIAddAccountView: View {
@State private var accountSecret: String = ""
@State private var accountAPIUrl: String = ""
@State private var showProgressIndicator: Bool = false
@State private var accountSetupError: (Error?, Bool) = (nil, false)
@State private var accountError: (Error?, Bool) = (nil, false)
var body: some View {
NavigationView {
@@ -32,7 +32,7 @@ struct ReaderAPIAddAccountView: View {
if accountType != nil {
AccountSectionHeader(accountType: accountType!)
}
accountDetailsSection
accountDetails
Section(footer: readerAccountExplainer) {}
}
.navigationTitle(Text(accountType?.localizedAccountName() ?? ""))
@@ -43,20 +43,22 @@ struct ReaderAPIAddAccountView: View {
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: { dismiss() }, label: { Text("CANCEL_BUTTON_TITLE", tableName: "Buttons") })
.disabled(showProgressIndicator)
}
ToolbarItem(placement: .navigationBarTrailing) {
if showProgressIndicator { ProgressView() }
}
}
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountSetupError.1) {
.alert(Text("ERROR_TITLE", tableName: "Errors"), isPresented: $accountError.1) {
Button(role: .cancel) {
//
} label: {
Text("DISMISS_BUTTON_TITLE", tableName: "Buttons")
}
} message: {
Text(accountSetupError.0?.localizedDescription ?? "")
Text(accountError.0?.localizedDescription ?? "")
}
.interactiveDismissDisabled(showProgressIndicator)
.dismissOnExternalContextLaunch()
.dismissOnAccountAdd()
}
@@ -80,7 +82,7 @@ struct ReaderAPIAddAccountView: View {
var accountDetailsSection: some View {
var accountDetails: some View {
Group {
Section {
TextField("Username", text: $accountUserName)
@@ -93,32 +95,39 @@ struct ReaderAPIAddAccountView: View {
.autocapitalization(.none)
}
}
Section {
Button {
Task {
do {
}
}
var accountButton: some View {
Section {
Button {
Task {
do {
if account == nil {
// Create a new account
try await executeAccountCredentials()
} else {
// Updating account credentials
try await executeAccountCredentials()
dismiss()
} catch {
accountSetupError = (error, true)
}
}
} label: {
HStack {
Spacer()
if accountCredentials == nil {
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
} else {
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
}
Spacer()
} catch {
accountError = (error, true)
}
}
.disabled(!validateCredentials())
} label: {
HStack {
Spacer()
if accountCredentials == nil {
Text("ADD_ACCOUNT_BUTTON_TITLE", tableName: "Buttons")
} else {
Text("UPDATE_CREDENTIALS_BUTTON_TITLE", tableName: "Buttons")
}
Spacer()
}
}
.disabled(!validateCredentials())
}
}
// MARK: - API
@@ -132,7 +141,7 @@ struct ReaderAPIAddAccountView: View {
accountSecret = creds.secret
}
} catch {
accountSetupError = (error, true)
accountError = (error, true)
}
}
}
@@ -152,7 +161,6 @@ struct ReaderAPIAddAccountView: View {
return true
}
@MainActor
private func executeAccountCredentials() async throws {
let trimmedAccountUserName = accountUserName.trimmingWhitespace
@@ -185,19 +193,23 @@ struct ReaderAPIAddAccountView: View {
case .success:
showProgressIndicator = false
continuation.resume()
return
case .failure(let error):
showProgressIndicator = false
continuation.resume(throwing: error)
return
}
})
} catch {
showProgressIndicator = false
continuation.resume(throwing: error)
continuation.resume(throwing: LocalizedNetNewsWireError.keychainError)
return
}
}
case .failure(let failure):
showProgressIndicator = false
continuation.resume(throwing: failure)
return
}
}
}

View File

@@ -1169,15 +1169,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
func showAccountInspector(for account: Account) {
let hosting = UIHostingController(rootView: InjectedNavigationView(injectedView: AccountInspectorView(account: account)))
rootSplitViewController.present(hosting, animated: true, completion: nil)
// let accountInspectorNavController =
// UIStoryboard.inspector.instantiateViewController(identifier: "AccountInspectorNavigationViewController") as! UINavigationController
// let accountInspectorController = accountInspectorNavController.topViewController as! AccountInspectorViewController
// accountInspectorNavController.modalPresentationStyle = .formSheet
// accountInspectorNavController.preferredContentSize = AccountInspectorViewController.preferredContentSizeForFormSheetDisplay
// accountInspectorController.isModal = true
// accountInspectorController.account = account
// rootSplitViewController.present(accountInspectorNavController, animated: true)
}
func showFeedInspector() {

View File

@@ -105,7 +105,9 @@ struct AccountsManagementView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
Text(account.nameForDisplay)
}.swipeActions(edge: .trailing, allowsFullSwipe: false) {
}
.transition(.move(edge: .top))
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
if account != AccountManager.shared.defaultAccount {
Button(role: .destructive) {
accountToRemove.wrappedValue = account
@@ -118,17 +120,19 @@ struct AccountsManagementView: View {
}
}.tint(.red)
}
Button {
withAnimation {
account.isActive.toggle()
}
} label: {
if account.isActive {
Image(systemName: "minus.circle")
} else {
Image(systemName: "togglepower")
}
}.tint(account.isActive ? .yellow : Color(uiColor: AppAssets.primaryAccentColor))
}
}
var inactiveFooterText: some View {
if AccountManager.shared.sortedAccounts.filter({ $0.isActive == false }).count == 0 {
return Text("NO_INACTIVE_ACCOUNT_FOOTER", tableName: "Settings")
} else {
return Text("")
}
}
}
struct AddAccountView_Previews: PreviewProvider {

View File

@@ -22,6 +22,9 @@ struct DismissOnAccountAdd: ViewModifier {
}
extension View {
/// Convenience modifier to dismiss a view when an account has been added.
/// - Returns: `View`
func dismissOnAccountAdd() -> some View {
modifier(DismissOnAccountAdd())
}

View File

@@ -23,6 +23,10 @@ struct DismissOnExternalContext: ViewModifier {
}
extension View {
/// This function dismisses a view when the user launches from
/// an external action, for example, opening the app from the widget.
/// - Returns: `View`
func dismissOnExternalContextLaunch() -> some View {
modifier(DismissOnExternalContext())
}